@serve.zone/dcrouter 13.38.4 → 13.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deno.json +1 -1
- package/dist_serve/bundle.js +350 -343
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/db/documents/classes.remote-ingress-edge.doc.d.ts +2 -0
- package/dist_ts/db/documents/classes.remote-ingress-edge.doc.js +8 -2
- package/dist_ts/http3/http3-route-augmentation.d.ts +1 -1
- package/dist_ts/http3/http3-route-augmentation.js +3 -17
- package/dist_ts/opsserver/handlers/remoteingress.handler.js +3 -2
- package/dist_ts/radius/classes.radius.server.d.ts +4 -4
- package/dist_ts/radius/classes.radius.server.js +80 -66
- package/dist_ts/remoteingress/classes.remoteingress-manager.d.ts +4 -2
- package/dist_ts/remoteingress/classes.remoteingress-manager.js +9 -21
- package/dist_ts_interfaces/data/remoteingress.d.ts +10 -0
- package/dist_ts_interfaces/requests/remoteingress.d.ts +3 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +2 -0
- package/dist_ts_web/appstate.js +3 -1
- package/dist_ts_web/elements/network/ops-view-remoteingress.d.ts +2 -0
- package/dist_ts_web/elements/network/ops-view-remoteingress.js +55 -2
- package/dist_ts_web/elements/overview/ops-view-config.js +4 -1
- package/package.json +3 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/db/documents/classes.remote-ingress-edge.doc.ts +4 -0
- package/ts/http3/http3-route-augmentation.ts +2 -18
- package/ts/opsserver/handlers/remoteingress.handler.ts +2 -0
- package/ts/radius/classes.radius.server.ts +90 -66
- package/ts/remoteingress/classes.remoteingress-manager.ts +12 -21
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +4 -0
- package/ts_web/elements/network/ops-view-remoteingress.ts +57 -1
- package/ts_web/elements/overview/ops-view-config.ts +10 -0
|
@@ -1,29 +1,13 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
|
-
import type { IRemoteIngress, IDcRouterRouteConfig } from '../../ts_interfaces/data/remoteingress.js';
|
|
2
|
+
import type { IRemoteIngress, IRemoteIngressPerformanceConfig, IDcRouterRouteConfig } from '../../ts_interfaces/data/remoteingress.js';
|
|
3
3
|
import { RemoteIngressEdgeDoc } from '../db/index.js';
|
|
4
4
|
|
|
5
5
|
interface IRemoteIngressFirewallConfig {
|
|
6
6
|
blockedIps?: string[];
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
*/
|
|
12
|
-
function extractPorts(portRange: number | Array<number | { from: number; to: number }>): number[] {
|
|
13
|
-
const ports = new Set<number>();
|
|
14
|
-
if (typeof portRange === 'number') {
|
|
15
|
-
ports.add(portRange);
|
|
16
|
-
} else if (Array.isArray(portRange)) {
|
|
17
|
-
for (const entry of portRange) {
|
|
18
|
-
if (typeof entry === 'number') {
|
|
19
|
-
ports.add(entry);
|
|
20
|
-
} else if (typeof entry === 'object' && 'from' in entry && 'to' in entry) {
|
|
21
|
-
for (let p = entry.from; p <= entry.to; p++) {
|
|
22
|
-
ports.add(p);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
9
|
+
function extractPorts(portRange: plugins.smartproxy.IRouteConfig['match']['ports']): number[] {
|
|
10
|
+
const ports = new Set<number>(plugins.smartproxy.expandPortRange(portRange) as number[]);
|
|
27
11
|
return [...ports].sort((a, b) => a - b);
|
|
28
12
|
}
|
|
29
13
|
|
|
@@ -59,6 +43,7 @@ export class RemoteIngressManager {
|
|
|
59
43
|
listenPortsUdp: doc.listenPortsUdp,
|
|
60
44
|
enabled: doc.enabled,
|
|
61
45
|
autoDerivePorts: doc.autoDerivePorts,
|
|
46
|
+
performance: doc.performance,
|
|
62
47
|
tags: doc.tags,
|
|
63
48
|
createdAt: doc.createdAt,
|
|
64
49
|
updatedAt: doc.updatedAt,
|
|
@@ -189,6 +174,7 @@ export class RemoteIngressManager {
|
|
|
189
174
|
listenPorts: number[] = [],
|
|
190
175
|
tags?: string[],
|
|
191
176
|
autoDerivePorts: boolean = true,
|
|
177
|
+
performance?: IRemoteIngressPerformanceConfig,
|
|
192
178
|
): Promise<IRemoteIngress> {
|
|
193
179
|
const id = plugins.uuid.v4();
|
|
194
180
|
const secret = plugins.crypto.randomBytes(32).toString('hex');
|
|
@@ -201,6 +187,7 @@ export class RemoteIngressManager {
|
|
|
201
187
|
listenPorts,
|
|
202
188
|
enabled: true,
|
|
203
189
|
autoDerivePorts,
|
|
190
|
+
performance,
|
|
204
191
|
tags: tags || [],
|
|
205
192
|
createdAt: now,
|
|
206
193
|
updatedAt: now,
|
|
@@ -237,6 +224,7 @@ export class RemoteIngressManager {
|
|
|
237
224
|
listenPorts?: number[];
|
|
238
225
|
autoDerivePorts?: boolean;
|
|
239
226
|
enabled?: boolean;
|
|
227
|
+
performance?: IRemoteIngressPerformanceConfig;
|
|
240
228
|
tags?: string[];
|
|
241
229
|
},
|
|
242
230
|
): Promise<IRemoteIngress | null> {
|
|
@@ -249,6 +237,7 @@ export class RemoteIngressManager {
|
|
|
249
237
|
if (updates.listenPorts !== undefined) edge.listenPorts = updates.listenPorts;
|
|
250
238
|
if (updates.autoDerivePorts !== undefined) edge.autoDerivePorts = updates.autoDerivePorts;
|
|
251
239
|
if (updates.enabled !== undefined) edge.enabled = updates.enabled;
|
|
240
|
+
if (updates.performance !== undefined) edge.performance = updates.performance;
|
|
252
241
|
if (updates.tags !== undefined) edge.tags = updates.tags;
|
|
253
242
|
edge.updatedAt = Date.now();
|
|
254
243
|
|
|
@@ -317,17 +306,19 @@ export class RemoteIngressManager {
|
|
|
317
306
|
* Get the list of allowed edges (enabled only) for the Rust hub.
|
|
318
307
|
* Includes listenPortsUdp when routes with transport 'udp' or 'all' are present.
|
|
319
308
|
*/
|
|
320
|
-
public getAllowedEdges(): Array<{ id: string; secret: string; listenPorts: number[]; listenPortsUdp?: number[]; firewallConfig?: IRemoteIngressFirewallConfig }> {
|
|
321
|
-
const result: Array<{ id: string; secret: string; listenPorts: number[]; listenPortsUdp?: number[]; firewallConfig?: IRemoteIngressFirewallConfig }> = [];
|
|
309
|
+
public getAllowedEdges(): Array<{ id: string; secret: string; listenPorts: number[]; listenPortsUdp?: number[]; firewallConfig?: IRemoteIngressFirewallConfig; performance?: IRemoteIngressPerformanceConfig }> {
|
|
310
|
+
const result: Array<{ id: string; secret: string; listenPorts: number[]; listenPortsUdp?: number[]; firewallConfig?: IRemoteIngressFirewallConfig; performance?: IRemoteIngressPerformanceConfig }> = [];
|
|
322
311
|
for (const edge of this.edges.values()) {
|
|
323
312
|
if (edge.enabled) {
|
|
324
313
|
const listenPortsUdp = this.getEffectiveListenPortsUdp(edge);
|
|
314
|
+
const performance = edge.performance && Object.keys(edge.performance).length > 0 ? edge.performance : undefined;
|
|
325
315
|
result.push({
|
|
326
316
|
id: edge.id,
|
|
327
317
|
secret: edge.secret,
|
|
328
318
|
listenPorts: this.getEffectiveListenPorts(edge),
|
|
329
319
|
...(listenPortsUdp.length > 0 ? { listenPortsUdp } : {}),
|
|
330
320
|
...(this.firewallConfig ? { firewallConfig: this.firewallConfig } : {}),
|
|
321
|
+
...(performance ? { performance } : {}),
|
|
331
322
|
});
|
|
332
323
|
}
|
|
333
324
|
}
|
package/ts_web/appstate.ts
CHANGED
|
@@ -1120,6 +1120,7 @@ export const createRemoteIngressAction = remoteIngressStatePart.createAction<{
|
|
|
1120
1120
|
name: string;
|
|
1121
1121
|
listenPorts?: number[];
|
|
1122
1122
|
autoDerivePorts?: boolean;
|
|
1123
|
+
performance?: interfaces.data.IRemoteIngressPerformanceConfig;
|
|
1123
1124
|
tags?: string[];
|
|
1124
1125
|
}>(async (statePartArg, dataArg, actionContext): Promise<IRemoteIngressState> => {
|
|
1125
1126
|
const context = getActionContext();
|
|
@@ -1135,6 +1136,7 @@ export const createRemoteIngressAction = remoteIngressStatePart.createAction<{
|
|
|
1135
1136
|
name: dataArg.name,
|
|
1136
1137
|
listenPorts: dataArg.listenPorts,
|
|
1137
1138
|
autoDerivePorts: dataArg.autoDerivePorts,
|
|
1139
|
+
performance: dataArg.performance,
|
|
1138
1140
|
tags: dataArg.tags,
|
|
1139
1141
|
});
|
|
1140
1142
|
|
|
@@ -1187,6 +1189,7 @@ export const updateRemoteIngressAction = remoteIngressStatePart.createAction<{
|
|
|
1187
1189
|
name?: string;
|
|
1188
1190
|
listenPorts?: number[];
|
|
1189
1191
|
autoDerivePorts?: boolean;
|
|
1192
|
+
performance?: interfaces.data.IRemoteIngressPerformanceConfig;
|
|
1190
1193
|
tags?: string[];
|
|
1191
1194
|
}>(async (statePartArg, dataArg, actionContext): Promise<IRemoteIngressState> => {
|
|
1192
1195
|
const context = getActionContext();
|
|
@@ -1203,6 +1206,7 @@ export const updateRemoteIngressAction = remoteIngressStatePart.createAction<{
|
|
|
1203
1206
|
name: dataArg.name,
|
|
1204
1207
|
listenPorts: dataArg.listenPorts,
|
|
1205
1208
|
autoDerivePorts: dataArg.autoDerivePorts,
|
|
1209
|
+
performance: dataArg.performance,
|
|
1206
1210
|
tags: dataArg.tags,
|
|
1207
1211
|
});
|
|
1208
1212
|
|
|
@@ -242,6 +242,7 @@ export class OpsViewRemoteIngress extends DeesElement {
|
|
|
242
242
|
publicIp: this.getEdgePublicIp(edge.id),
|
|
243
243
|
ports: this.getPortsHtml(edge),
|
|
244
244
|
tunnels: this.getEdgeTunnelCount(edge.id),
|
|
245
|
+
maxConnections: this.getMaxConnectionsHtml(edge),
|
|
245
246
|
window: this.getWindowHtml(edge.id),
|
|
246
247
|
queues: this.getQueuesHtml(edge.id),
|
|
247
248
|
traffic: this.getTrafficHtml(edge.id),
|
|
@@ -261,6 +262,7 @@ export class OpsViewRemoteIngress extends DeesElement {
|
|
|
261
262
|
<dees-input-text .key=${'name'} .label=${'Name'} .required=${true}></dees-input-text>
|
|
262
263
|
<dees-input-text .key=${'listenPorts'} .label=${'Manual Ports'} .description=${'Comma-separated port numbers, optional'}></dees-input-text>
|
|
263
264
|
<dees-input-checkbox .key=${'autoDerivePorts'} .label=${'Auto-derive ports from routes'} .value=${true}></dees-input-checkbox>
|
|
265
|
+
<dees-input-text .key=${'maxStreamsPerEdge'} .label=${'Max Connections'} .description=${'Optional maximum concurrent client connections for this edge. Leave empty to use the hub default.'}></dees-input-text>
|
|
264
266
|
<dees-input-text .key=${'tags'} .label=${'Tags'} .description=${'Comma-separated, optional'}></dees-input-text>
|
|
265
267
|
</dees-form>
|
|
266
268
|
`,
|
|
@@ -284,12 +286,20 @@ export class OpsViewRemoteIngress extends DeesElement {
|
|
|
284
286
|
? portsStr.split(',').map((p: string) => parseInt(p.trim(), 10)).filter((p: number) => !isNaN(p))
|
|
285
287
|
: undefined;
|
|
286
288
|
const autoDerivePorts = formData.autoDerivePorts !== false;
|
|
289
|
+
let performance: interfaces.data.IRemoteIngressPerformanceConfig | undefined;
|
|
290
|
+
try {
|
|
291
|
+
performance = this.collectPerformanceOverride(formData);
|
|
292
|
+
} catch (err: unknown) {
|
|
293
|
+
const { DeesToast } = await import('@design.estate/dees-catalog');
|
|
294
|
+
DeesToast.show({ message: (err as Error).message, type: 'error', duration: 4000 });
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
287
297
|
const tags = formData.tags
|
|
288
298
|
? formData.tags.split(',').map((t: string) => t.trim()).filter(Boolean)
|
|
289
299
|
: undefined;
|
|
290
300
|
await appstate.remoteIngressStatePart.dispatchAction(
|
|
291
301
|
appstate.createRemoteIngressAction,
|
|
292
|
-
{ name, listenPorts, autoDerivePorts, tags },
|
|
302
|
+
{ name, listenPorts, autoDerivePorts, performance, tags },
|
|
293
303
|
);
|
|
294
304
|
await modalArg.destroy();
|
|
295
305
|
},
|
|
@@ -338,6 +348,7 @@ export class OpsViewRemoteIngress extends DeesElement {
|
|
|
338
348
|
<dees-input-text .key=${'name'} .label=${'Name'} .value=${edge.name}></dees-input-text>
|
|
339
349
|
<dees-input-text .key=${'listenPorts'} .label=${'Manual Ports'} .description=${'Comma-separated port numbers'} .value=${(edge.listenPorts || []).join(', ')}></dees-input-text>
|
|
340
350
|
<dees-input-checkbox .key=${'autoDerivePorts'} .label=${'Auto-derive ports from routes'} .value=${edge.autoDerivePorts !== false}></dees-input-checkbox>
|
|
351
|
+
<dees-input-text .key=${'maxStreamsPerEdge'} .label=${'Max Connections'} .description=${'Optional maximum concurrent client connections for this edge. Leave empty to use the hub default.'} .value=${edge.performance?.maxStreamsPerEdge?.toString() || ''}></dees-input-text>
|
|
341
352
|
<dees-input-text .key=${'tags'} .label=${'Tags'} .description=${'Comma-separated'} .value=${(edge.tags || []).join(', ')}></dees-input-text>
|
|
342
353
|
</dees-form>
|
|
343
354
|
`,
|
|
@@ -359,6 +370,14 @@ export class OpsViewRemoteIngress extends DeesElement {
|
|
|
359
370
|
? portsStr.split(',').map((p: string) => parseInt(p.trim(), 10)).filter((p: number) => !isNaN(p))
|
|
360
371
|
: [];
|
|
361
372
|
const autoDerivePorts = formData.autoDerivePorts !== false;
|
|
373
|
+
let performance: interfaces.data.IRemoteIngressPerformanceConfig | undefined;
|
|
374
|
+
try {
|
|
375
|
+
performance = this.collectPerformanceOverride(formData, edge.performance);
|
|
376
|
+
} catch (err: unknown) {
|
|
377
|
+
const { DeesToast } = await import('@design.estate/dees-catalog');
|
|
378
|
+
DeesToast.show({ message: (err as Error).message, type: 'error', duration: 4000 });
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
362
381
|
const tags = formData.tags
|
|
363
382
|
? formData.tags.split(',').map((t: string) => t.trim()).filter(Boolean)
|
|
364
383
|
: [];
|
|
@@ -369,6 +388,7 @@ export class OpsViewRemoteIngress extends DeesElement {
|
|
|
369
388
|
name: formData.name || edge.name,
|
|
370
389
|
listenPorts,
|
|
371
390
|
autoDerivePorts,
|
|
391
|
+
performance,
|
|
372
392
|
tags,
|
|
373
393
|
},
|
|
374
394
|
);
|
|
@@ -475,6 +495,19 @@ export class OpsViewRemoteIngress extends DeesElement {
|
|
|
475
495
|
return status?.activeTunnels || 0;
|
|
476
496
|
}
|
|
477
497
|
|
|
498
|
+
private getMaxConnectionsHtml(edge: interfaces.data.IRemoteIngress): TemplateResult | string {
|
|
499
|
+
const status = this.getEdgeStatus(edge.id);
|
|
500
|
+
const override = edge.performance?.maxStreamsPerEdge;
|
|
501
|
+
const effective = status?.performance?.maxStreamsPerEdge;
|
|
502
|
+
if (!override && !effective) return '-';
|
|
503
|
+
return html`
|
|
504
|
+
<div class="metricStack">
|
|
505
|
+
<span>${override || effective}</span>
|
|
506
|
+
<span class="metricMuted">${override ? 'edge override' : 'hub default'}</span>
|
|
507
|
+
</div>
|
|
508
|
+
`;
|
|
509
|
+
}
|
|
510
|
+
|
|
478
511
|
private getTransportHtml(edgeId: string): TemplateResult | string {
|
|
479
512
|
const status = this.getEdgeStatus(edgeId);
|
|
480
513
|
if (!status?.connected) return '-';
|
|
@@ -535,4 +568,27 @@ export class OpsViewRemoteIngress extends DeesElement {
|
|
|
535
568
|
}
|
|
536
569
|
return `${value >= 10 || unitIndex === 0 ? value.toFixed(0) : value.toFixed(1)} ${units[unitIndex]}`;
|
|
537
570
|
}
|
|
571
|
+
|
|
572
|
+
private collectPerformanceOverride(
|
|
573
|
+
formData: Record<string, any>,
|
|
574
|
+
base?: interfaces.data.IRemoteIngressPerformanceConfig,
|
|
575
|
+
): interfaces.data.IRemoteIngressPerformanceConfig | undefined {
|
|
576
|
+
const next: interfaces.data.IRemoteIngressPerformanceConfig = { ...(base || {}) };
|
|
577
|
+
const maxStreamsText = `${formData.maxStreamsPerEdge || ''}`.trim();
|
|
578
|
+
if (maxStreamsText) {
|
|
579
|
+
const maxStreamsPerEdge = Number.parseInt(maxStreamsText, 10);
|
|
580
|
+
if (!Number.isInteger(maxStreamsPerEdge) || maxStreamsPerEdge < 1) {
|
|
581
|
+
throw new Error('Max Connections must be a positive integer');
|
|
582
|
+
}
|
|
583
|
+
next.maxStreamsPerEdge = maxStreamsPerEdge;
|
|
584
|
+
} else {
|
|
585
|
+
delete next.maxStreamsPerEdge;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (Object.keys(next).length > 0) {
|
|
589
|
+
return next;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return base ? {} : undefined;
|
|
593
|
+
}
|
|
538
594
|
}
|
|
@@ -304,6 +304,16 @@ export class OpsViewConfig extends DeesElement {
|
|
|
304
304
|
{ key: 'Connected Edge IPs', value: ri.connectedEdgeIps?.length > 0 ? ri.connectedEdgeIps : null, type: 'pills' },
|
|
305
305
|
];
|
|
306
306
|
|
|
307
|
+
if (ri.performance) {
|
|
308
|
+
fields.push(
|
|
309
|
+
{ key: 'Performance Profile', value: ri.performance.profile || null, type: 'badge' },
|
|
310
|
+
{ key: 'Max Connections / Edge', value: ri.performance.maxStreamsPerEdge ?? null },
|
|
311
|
+
{ key: 'Client Write Timeout', value: ri.performance.clientWriteTimeoutMs ? `${ri.performance.clientWriteTimeoutMs} ms` : null },
|
|
312
|
+
{ key: 'First Data Timeout', value: ri.performance.firstDataConnectTimeoutMs ? `${ri.performance.firstDataConnectTimeoutMs} ms` : null },
|
|
313
|
+
{ key: 'Server-first Ports', value: ri.performance.serverFirstPorts?.length ? ri.performance.serverFirstPorts.map(String) : null, type: 'pills' },
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
307
317
|
const actions: IConfigSectionAction[] = [
|
|
308
318
|
{ label: 'View Remote Ingress', icon: 'lucide:arrow-right', event: 'navigate', detail: { view: 'network', subview: 'remoteingress' } },
|
|
309
319
|
];
|