@serve.zone/dcrouter 8.0.0 → 9.1.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.
Files changed (88) hide show
  1. package/dist_serve/bundle.js +2420 -1227
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/classes.dcrouter.d.ts +9 -0
  4. package/dist_ts/classes.dcrouter.js +27 -1
  5. package/dist_ts/config/classes.api-token-manager.d.ts +38 -0
  6. package/dist_ts/config/classes.api-token-manager.js +134 -0
  7. package/dist_ts/config/classes.route-config-manager.d.ts +35 -0
  8. package/dist_ts/config/classes.route-config-manager.js +231 -0
  9. package/dist_ts/config/index.d.ts +2 -0
  10. package/dist_ts/config/index.js +3 -1
  11. package/dist_ts/opsserver/classes.opsserver.d.ts +2 -0
  12. package/dist_ts/opsserver/classes.opsserver.js +5 -1
  13. package/dist_ts/opsserver/handlers/{config.handler.d.ts → api-token.handler.d.ts} +5 -2
  14. package/dist_ts/opsserver/handlers/api-token.handler.js +66 -0
  15. package/dist_ts/opsserver/handlers/index.d.ts +2 -0
  16. package/dist_ts/opsserver/handlers/index.js +3 -1
  17. package/dist_ts/opsserver/handlers/route-management.handler.d.ts +13 -0
  18. package/dist_ts/opsserver/handlers/route-management.handler.js +117 -0
  19. package/dist_ts_interfaces/data/index.d.ts +1 -0
  20. package/dist_ts_interfaces/data/index.js +2 -1
  21. package/dist_ts_interfaces/data/route-management.d.ts +68 -0
  22. package/dist_ts_interfaces/data/route-management.js +2 -0
  23. package/dist_ts_interfaces/requests/api-tokens.d.ts +63 -0
  24. package/dist_ts_interfaces/requests/api-tokens.js +2 -0
  25. package/dist_ts_interfaces/requests/config.d.ts +77 -1
  26. package/dist_ts_interfaces/requests/index.d.ts +2 -0
  27. package/dist_ts_interfaces/requests/index.js +3 -1
  28. package/dist_ts_interfaces/requests/route-management.d.ts +114 -0
  29. package/dist_ts_interfaces/requests/route-management.js +2 -0
  30. package/dist_ts_web/00_commitinfo_data.js +1 -1
  31. package/dist_ts_web/appstate.d.ts +37 -1
  32. package/dist_ts_web/appstate.js +220 -2
  33. package/dist_ts_web/elements/index.d.ts +2 -0
  34. package/dist_ts_web/elements/index.js +3 -1
  35. package/dist_ts_web/elements/ops-dashboard.js +23 -3
  36. package/dist_ts_web/elements/ops-view-apitokens.d.ts +12 -0
  37. package/dist_ts_web/elements/ops-view-apitokens.js +310 -0
  38. package/dist_ts_web/elements/ops-view-config.d.ts +10 -8
  39. package/dist_ts_web/elements/ops-view-config.js +215 -297
  40. package/dist_ts_web/elements/ops-view-routes.d.ts +12 -0
  41. package/dist_ts_web/elements/ops-view-routes.js +404 -0
  42. package/dist_ts_web/router.d.ts +1 -1
  43. package/dist_ts_web/router.js +2 -2
  44. package/package.json +2 -2
  45. package/ts/00_commitinfo_data.ts +1 -1
  46. package/ts/classes.dcrouter.ts +37 -1
  47. package/ts/config/classes.api-token-manager.ts +155 -0
  48. package/ts/config/classes.route-config-manager.ts +271 -0
  49. package/ts/config/index.ts +3 -1
  50. package/ts/opsserver/classes.opsserver.ts +4 -0
  51. package/ts/opsserver/handlers/api-token.handler.ts +96 -0
  52. package/ts/opsserver/handlers/config.handler.ts +154 -72
  53. package/ts/opsserver/handlers/index.ts +3 -1
  54. package/ts/opsserver/handlers/route-management.handler.ts +163 -0
  55. package/ts_web/00_commitinfo_data.ts +1 -1
  56. package/ts_web/appstate.ts +309 -2
  57. package/ts_web/elements/index.ts +2 -0
  58. package/ts_web/elements/ops-dashboard.ts +22 -2
  59. package/ts_web/elements/ops-view-apitokens.ts +285 -0
  60. package/ts_web/elements/ops-view-config.ts +237 -299
  61. package/ts_web/elements/ops-view-routes.ts +389 -0
  62. package/ts_web/router.ts +1 -1
  63. package/dist_ts/cache/classes.cache.cleaner.d.ts +0 -47
  64. package/dist_ts/cache/classes.cache.cleaner.js +0 -130
  65. package/dist_ts/cache/classes.cached.document.d.ts +0 -76
  66. package/dist_ts/cache/classes.cached.document.js +0 -100
  67. package/dist_ts/cache/classes.cachedb.d.ts +0 -60
  68. package/dist_ts/cache/classes.cachedb.js +0 -126
  69. package/dist_ts/cache/documents/classes.cached.email.d.ts +0 -125
  70. package/dist_ts/cache/documents/classes.cached.email.js +0 -337
  71. package/dist_ts/cache/documents/classes.cached.ip.reputation.d.ts +0 -119
  72. package/dist_ts/cache/documents/classes.cached.ip.reputation.js +0 -323
  73. package/dist_ts/cache/documents/index.d.ts +0 -2
  74. package/dist_ts/cache/documents/index.js +0 -3
  75. package/dist_ts/cache/index.d.ts +0 -4
  76. package/dist_ts/cache/index.js +0 -7
  77. package/dist_ts/monitoring/classes.metricscache.d.ts +0 -32
  78. package/dist_ts/monitoring/classes.metricscache.js +0 -63
  79. package/dist_ts/opsserver/handlers/admin.handler.d.ts +0 -31
  80. package/dist_ts/opsserver/handlers/admin.handler.js +0 -180
  81. package/dist_ts/opsserver/handlers/config.handler.js +0 -67
  82. package/dist_ts/opsserver/handlers/logs.handler.d.ts +0 -17
  83. package/dist_ts/opsserver/handlers/logs.handler.js +0 -215
  84. package/dist_ts/security/classes.securitylogger.js +0 -235
  85. package/dist_ts/storage/classes.storagemanager.d.ts +0 -82
  86. package/dist_ts/storage/classes.storagemanager.js +0 -344
  87. package/dist_ts/storage/index.d.ts +0 -1
  88. package/dist_ts/storage/index.js +0 -3
@@ -0,0 +1,404 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import * as appstate from '../appstate.js';
36
+ import * as interfaces from '../../dist_ts_interfaces/index.js';
37
+ import { viewHostCss } from './shared/css.js';
38
+ import {} from '@design.estate/dees-catalog';
39
+ import { DeesElement, css, cssManager, customElement, html, state, } from '@design.estate/dees-element';
40
+ let OpsViewRoutes = (() => {
41
+ let _classDecorators = [customElement('ops-view-routes')];
42
+ let _classDescriptor;
43
+ let _classExtraInitializers = [];
44
+ let _classThis;
45
+ let _classSuper = DeesElement;
46
+ let _routeState_decorators;
47
+ let _routeState_initializers = [];
48
+ let _routeState_extraInitializers = [];
49
+ var OpsViewRoutes = class extends _classSuper {
50
+ static { _classThis = this; }
51
+ static {
52
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
53
+ _routeState_decorators = [state()];
54
+ __esDecorate(this, null, _routeState_decorators, { kind: "accessor", name: "routeState", static: false, private: false, access: { has: obj => "routeState" in obj, get: obj => obj.routeState, set: (obj, value) => { obj.routeState = value; } }, metadata: _metadata }, _routeState_initializers, _routeState_extraInitializers);
55
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
56
+ OpsViewRoutes = _classThis = _classDescriptor.value;
57
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
58
+ }
59
+ #routeState_accessor_storage = __runInitializers(this, _routeState_initializers, {
60
+ mergedRoutes: [],
61
+ warnings: [],
62
+ apiTokens: [],
63
+ isLoading: false,
64
+ error: null,
65
+ lastUpdated: 0,
66
+ });
67
+ get routeState() { return this.#routeState_accessor_storage; }
68
+ set routeState(value) { this.#routeState_accessor_storage = value; }
69
+ constructor() {
70
+ super();
71
+ __runInitializers(this, _routeState_extraInitializers);
72
+ const sub = appstate.routeManagementStatePart
73
+ .select((s) => s)
74
+ .subscribe((routeState) => {
75
+ this.routeState = routeState;
76
+ });
77
+ this.rxSubscriptions.push(sub);
78
+ // Re-fetch routes when user logs in (fixes race condition where
79
+ // the view is created before authentication completes)
80
+ const loginSub = appstate.loginStatePart
81
+ .select((s) => s.isLoggedIn)
82
+ .subscribe((isLoggedIn) => {
83
+ if (isLoggedIn) {
84
+ appstate.routeManagementStatePart.dispatchAction(appstate.fetchMergedRoutesAction, null);
85
+ }
86
+ });
87
+ this.rxSubscriptions.push(loginSub);
88
+ }
89
+ static styles = [
90
+ cssManager.defaultStyles,
91
+ viewHostCss,
92
+ css `
93
+ .routesContainer {
94
+ display: flex;
95
+ flex-direction: column;
96
+ gap: 24px;
97
+ }
98
+
99
+ .warnings-bar {
100
+ background: ${cssManager.bdTheme('rgba(255, 170, 0, 0.08)', 'rgba(255, 170, 0, 0.1)')};
101
+ border: 1px solid ${cssManager.bdTheme('rgba(255, 170, 0, 0.25)', 'rgba(255, 170, 0, 0.3)')};
102
+ border-radius: 8px;
103
+ padding: 12px 16px;
104
+ }
105
+
106
+ .warning-item {
107
+ display: flex;
108
+ align-items: center;
109
+ gap: 8px;
110
+ padding: 4px 0;
111
+ font-size: 13px;
112
+ color: ${cssManager.bdTheme('#b45309', '#fa0')};
113
+ }
114
+
115
+ .warning-icon {
116
+ flex-shrink: 0;
117
+ }
118
+
119
+ .empty-state {
120
+ text-align: center;
121
+ padding: 48px 24px;
122
+ color: ${cssManager.bdTheme('#6b7280', '#666')};
123
+ }
124
+
125
+ .empty-state p {
126
+ margin: 8px 0;
127
+ }
128
+ `,
129
+ ];
130
+ render() {
131
+ const { mergedRoutes, warnings } = this.routeState;
132
+ const hardcodedCount = mergedRoutes.filter((mr) => mr.source === 'hardcoded').length;
133
+ const programmaticCount = mergedRoutes.filter((mr) => mr.source === 'programmatic').length;
134
+ const disabledCount = mergedRoutes.filter((mr) => !mr.enabled).length;
135
+ const statsTiles = [
136
+ {
137
+ id: 'totalRoutes',
138
+ title: 'Total Routes',
139
+ type: 'number',
140
+ value: mergedRoutes.length,
141
+ icon: 'lucide:route',
142
+ description: 'All configured routes',
143
+ color: '#3b82f6',
144
+ },
145
+ {
146
+ id: 'hardcoded',
147
+ title: 'Hardcoded',
148
+ type: 'number',
149
+ value: hardcodedCount,
150
+ icon: 'lucide:lock',
151
+ description: 'Routes from constructor config',
152
+ color: '#8b5cf6',
153
+ },
154
+ {
155
+ id: 'programmatic',
156
+ title: 'Programmatic',
157
+ type: 'number',
158
+ value: programmaticCount,
159
+ icon: 'lucide:code',
160
+ description: 'Routes added via API',
161
+ color: '#0ea5e9',
162
+ },
163
+ {
164
+ id: 'disabled',
165
+ title: 'Disabled',
166
+ type: 'number',
167
+ value: disabledCount,
168
+ icon: 'lucide:pauseCircle',
169
+ description: 'Currently disabled routes',
170
+ color: disabledCount > 0 ? '#ef4444' : '#6b7280',
171
+ },
172
+ ];
173
+ // Map merged routes to sz-route-list-view format
174
+ const szRoutes = mergedRoutes.map((mr) => {
175
+ const tags = [...(mr.route.tags || [])];
176
+ tags.push(mr.source);
177
+ if (!mr.enabled)
178
+ tags.push('disabled');
179
+ if (mr.overridden)
180
+ tags.push('overridden');
181
+ return {
182
+ ...mr.route,
183
+ enabled: mr.enabled,
184
+ tags,
185
+ id: mr.storedRouteId || mr.route.name || undefined,
186
+ };
187
+ });
188
+ return html `
189
+ <ops-sectionheading>Route Management</ops-sectionheading>
190
+
191
+ <div class="routesContainer">
192
+ <dees-statsgrid
193
+ .tiles=${statsTiles}
194
+ .gridActions=${[
195
+ {
196
+ name: 'Add Route',
197
+ iconName: 'lucide:plus',
198
+ action: () => this.showCreateRouteDialog(),
199
+ },
200
+ {
201
+ name: 'Refresh',
202
+ iconName: 'lucide:refreshCw',
203
+ action: () => this.refreshData(),
204
+ },
205
+ ]}
206
+ ></dees-statsgrid>
207
+
208
+ ${warnings.length > 0
209
+ ? html `
210
+ <div class="warnings-bar">
211
+ ${warnings.map((w) => html `
212
+ <div class="warning-item">
213
+ <span class="warning-icon">&#9888;</span>
214
+ <span>${w.message}</span>
215
+ </div>
216
+ `)}
217
+ </div>
218
+ `
219
+ : ''}
220
+
221
+ ${szRoutes.length > 0
222
+ ? html `
223
+ <sz-route-list-view
224
+ .routes=${szRoutes}
225
+ @route-click=${(e) => this.handleRouteClick(e)}
226
+ ></sz-route-list-view>
227
+ `
228
+ : html `
229
+ <div class="empty-state">
230
+ <p>No routes configured</p>
231
+ <p>Add a programmatic route or check your constructor configuration.</p>
232
+ </div>
233
+ `}
234
+ </div>
235
+ `;
236
+ }
237
+ async handleRouteClick(e) {
238
+ const clickedRoute = e.detail;
239
+ if (!clickedRoute)
240
+ return;
241
+ // Find the corresponding merged route
242
+ const merged = this.routeState.mergedRoutes.find((mr) => mr.route.name === clickedRoute.name);
243
+ if (!merged)
244
+ return;
245
+ const { DeesModal } = await import('@design.estate/dees-catalog');
246
+ if (merged.source === 'hardcoded') {
247
+ const menuOptions = merged.enabled
248
+ ? [
249
+ {
250
+ name: 'Disable Route',
251
+ iconName: 'lucide:pause',
252
+ action: async (modalArg) => {
253
+ await appstate.routeManagementStatePart.dispatchAction(appstate.setRouteOverrideAction, { routeName: merged.route.name, enabled: false });
254
+ await modalArg.destroy();
255
+ },
256
+ },
257
+ {
258
+ name: 'Close',
259
+ iconName: 'lucide:x',
260
+ action: async (modalArg) => await modalArg.destroy(),
261
+ },
262
+ ]
263
+ : [
264
+ {
265
+ name: 'Enable Route',
266
+ iconName: 'lucide:play',
267
+ action: async (modalArg) => {
268
+ await appstate.routeManagementStatePart.dispatchAction(appstate.setRouteOverrideAction, { routeName: merged.route.name, enabled: true });
269
+ await modalArg.destroy();
270
+ },
271
+ },
272
+ {
273
+ name: 'Remove Override',
274
+ iconName: 'lucide:undo',
275
+ action: async (modalArg) => {
276
+ await appstate.routeManagementStatePart.dispatchAction(appstate.removeRouteOverrideAction, merged.route.name);
277
+ await modalArg.destroy();
278
+ },
279
+ },
280
+ {
281
+ name: 'Close',
282
+ iconName: 'lucide:x',
283
+ action: async (modalArg) => await modalArg.destroy(),
284
+ },
285
+ ];
286
+ await DeesModal.createAndShow({
287
+ heading: `Route: ${merged.route.name}`,
288
+ content: html `
289
+ <div style="color: #ccc; padding: 8px 0;">
290
+ <p>Source: <strong style="color: #88f;">hardcoded</strong></p>
291
+ <p>Status: <strong>${merged.enabled ? 'Enabled' : 'Disabled (overridden)'}</strong></p>
292
+ <p style="color: #888; font-size: 13px;">Hardcoded routes cannot be edited or deleted, but they can be disabled via an override.</p>
293
+ </div>
294
+ `,
295
+ menuOptions,
296
+ });
297
+ }
298
+ else {
299
+ // Programmatic route
300
+ await DeesModal.createAndShow({
301
+ heading: `Route: ${merged.route.name}`,
302
+ content: html `
303
+ <div style="color: #ccc; padding: 8px 0;">
304
+ <p>Source: <strong style="color: #0af;">programmatic</strong></p>
305
+ <p>Status: <strong>${merged.enabled ? 'Enabled' : 'Disabled'}</strong></p>
306
+ <p>ID: <code style="color: #888;">${merged.storedRouteId}</code></p>
307
+ </div>
308
+ `,
309
+ menuOptions: [
310
+ {
311
+ name: merged.enabled ? 'Disable' : 'Enable',
312
+ iconName: merged.enabled ? 'lucide:pause' : 'lucide:play',
313
+ action: async (modalArg) => {
314
+ await appstate.routeManagementStatePart.dispatchAction(appstate.toggleRouteAction, { id: merged.storedRouteId, enabled: !merged.enabled });
315
+ await modalArg.destroy();
316
+ },
317
+ },
318
+ {
319
+ name: 'Delete',
320
+ iconName: 'lucide:trash-2',
321
+ action: async (modalArg) => {
322
+ await appstate.routeManagementStatePart.dispatchAction(appstate.deleteRouteAction, merged.storedRouteId);
323
+ await modalArg.destroy();
324
+ },
325
+ },
326
+ {
327
+ name: 'Close',
328
+ iconName: 'lucide:x',
329
+ action: async (modalArg) => await modalArg.destroy(),
330
+ },
331
+ ],
332
+ });
333
+ }
334
+ }
335
+ async showCreateRouteDialog() {
336
+ const { DeesModal } = await import('@design.estate/dees-catalog');
337
+ await DeesModal.createAndShow({
338
+ heading: 'Add Programmatic Route',
339
+ content: html `
340
+ <dees-form>
341
+ <dees-input-text .key=${'name'} .label=${'Route Name'} .required=${true}></dees-input-text>
342
+ <dees-input-text .key=${'ports'} .label=${'Ports (comma-separated)'} .required=${true}></dees-input-text>
343
+ <dees-input-text .key=${'domains'} .label=${'Domains (comma-separated, optional)'}></dees-input-text>
344
+ <dees-input-text .key=${'targetHost'} .label=${'Target Host'} .value=${'localhost'} .required=${true}></dees-input-text>
345
+ <dees-input-text .key=${'targetPort'} .label=${'Target Port'} .required=${true}></dees-input-text>
346
+ </dees-form>
347
+ `,
348
+ menuOptions: [
349
+ {
350
+ name: 'Cancel',
351
+ iconName: 'lucide:x',
352
+ action: async (modalArg) => await modalArg.destroy(),
353
+ },
354
+ {
355
+ name: 'Create',
356
+ iconName: 'lucide:plus',
357
+ action: async (modalArg) => {
358
+ const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
359
+ if (!form)
360
+ return;
361
+ const formData = await form.collectFormData();
362
+ if (!formData.name || !formData.ports)
363
+ return;
364
+ const ports = formData.ports.split(',').map((p) => parseInt(p.trim(), 10)).filter((p) => !isNaN(p));
365
+ const domains = formData.domains
366
+ ? formData.domains.split(',').map((d) => d.trim()).filter(Boolean)
367
+ : undefined;
368
+ const route = {
369
+ name: formData.name,
370
+ match: {
371
+ ports,
372
+ ...(domains && domains.length > 0 ? { domains } : {}),
373
+ },
374
+ action: {
375
+ type: 'forward',
376
+ targets: [
377
+ {
378
+ host: formData.targetHost || 'localhost',
379
+ port: parseInt(formData.targetPort, 10),
380
+ },
381
+ ],
382
+ },
383
+ };
384
+ await appstate.routeManagementStatePart.dispatchAction(appstate.createRouteAction, { route });
385
+ await modalArg.destroy();
386
+ },
387
+ },
388
+ ],
389
+ });
390
+ }
391
+ refreshData() {
392
+ appstate.routeManagementStatePart.dispatchAction(appstate.fetchMergedRoutesAction, null);
393
+ }
394
+ async firstUpdated() {
395
+ await appstate.routeManagementStatePart.dispatchAction(appstate.fetchMergedRoutesAction, null);
396
+ }
397
+ static {
398
+ __runInitializers(_classThis, _classExtraInitializers);
399
+ }
400
+ };
401
+ return OpsViewRoutes = _classThis;
402
+ })();
403
+ export { OpsViewRoutes };
404
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BzLXZpZXctcm91dGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHNfd2ViL2VsZW1lbnRzL29wcy12aWV3LXJvdXRlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsT0FBTyxLQUFLLFFBQVEsTUFBTSxnQkFBZ0IsQ0FBQztBQUMzQyxPQUFPLEtBQUssVUFBVSxNQUFNLG1DQUFtQyxDQUFDO0FBQ2hFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUM5QyxPQUFPLEVBQW1CLE1BQU0sNkJBQTZCLENBQUM7QUFFOUQsT0FBTyxFQUNMLFdBQVcsRUFDWCxHQUFHLEVBQ0gsVUFBVSxFQUNWLGFBQWEsRUFDYixJQUFJLEVBQ0osS0FBSyxHQUVOLE1BQU0sNkJBQTZCLENBQUM7SUFHeEIsYUFBYTs0QkFEekIsYUFBYSxDQUFDLGlCQUFpQixDQUFDOzs7O3NCQUNFLFdBQVc7Ozs7NkJBQW5CLFNBQVEsV0FBVzs7OztzQ0FDM0MsS0FBSyxFQUFFO1lBQUMsbUxBQVMsVUFBVSw2QkFBVixVQUFVLCtGQU8xQjtZQVJKLDZLQW9YQzs7OztRQW5YVSxpRkFBc0Q7WUFDN0QsWUFBWSxFQUFFLEVBQUU7WUFDaEIsUUFBUSxFQUFFLEVBQUU7WUFDWixTQUFTLEVBQUUsRUFBRTtZQUNiLFNBQVMsRUFBRSxLQUFLO1lBQ2hCLEtBQUssRUFBRSxJQUFJO1lBQ1gsV0FBVyxFQUFFLENBQUM7U0FDZixFQUFDO1FBUE8sSUFBUyxVQUFVLGdEQU8xQjtRQVBPLElBQVMsVUFBVSxzREFPMUI7UUFFRjtZQUNFLEtBQUssRUFBRSxDQUFDOztZQUNSLE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyx3QkFBd0I7aUJBQzFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO2lCQUNoQixTQUFTLENBQUMsQ0FBQyxVQUFVLEVBQUUsRUFBRTtn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
@@ -1,4 +1,4 @@
1
- export declare const validViews: readonly ["overview", "network", "emails", "logs", "configuration", "security", "certificates", "remoteingress"];
1
+ export declare const validViews: readonly ["overview", "network", "emails", "logs", "routes", "apitokens", "configuration", "security", "certificates", "remoteingress"];
2
2
  export type TValidView = typeof validViews[number];
3
3
  declare class AppRouter {
4
4
  private router;
@@ -1,7 +1,7 @@
1
1
  import * as plugins from './plugins.js';
2
2
  import * as appstate from './appstate.js';
3
3
  const SmartRouter = plugins.domtools.plugins.smartrouter.SmartRouter;
4
- export const validViews = ['overview', 'network', 'emails', 'logs', 'configuration', 'security', 'certificates', 'remoteingress'];
4
+ export const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress'];
5
5
  class AppRouter {
6
6
  router;
7
7
  initialized = false;
@@ -88,4 +88,4 @@ class AppRouter {
88
88
  }
89
89
  }
90
90
  export const appRouter = new AppRouter();
91
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfd2ViL3JvdXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEtBQUssUUFBUSxNQUFNLGVBQWUsQ0FBQztBQUUxQyxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDO0FBRXJFLE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRyxDQUFDLFVBQVUsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxlQUFlLEVBQUUsVUFBVSxFQUFFLGNBQWMsRUFBRSxlQUFlLENBQVUsQ0FBQztBQUkzSSxNQUFNLFNBQVM7SUFDTCxNQUFNLENBQW1DO0lBQ3pDLFdBQVcsR0FBRyxLQUFLLENBQUM7SUFDcEIsbUJBQW1CLEdBQUcsS0FBSyxDQUFDO0lBRXBDO1FBQ0UsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLFdBQVcsQ0FBQyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO0lBQ2xELENBQUM7SUFFTSxJQUFJO1FBQ1QsSUFBSSxJQUFJLENBQUMsV0FBVztZQUFFLE9BQU87UUFDN0IsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ25CLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUN0QixJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztRQUMxQixJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQztJQUMxQixDQUFDO0lBRU8sV0FBVztRQUNqQixLQUFLLE1BQU0sSUFBSSxJQUFJLFVBQVUsRUFBRSxDQUFDO1lBQzlCLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLElBQUksSUFBSSxFQUFFLEVBQUUsS0FBSyxJQUFJLEVBQUU7Z0JBQ3BDLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLENBQUM7WUFD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
91
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfd2ViL3JvdXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEtBQUssUUFBUSxNQUFNLGVBQWUsQ0FBQztBQUUxQyxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDO0FBRXJFLE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRyxDQUFDLFVBQVUsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLGVBQWUsRUFBRSxVQUFVLEVBQUUsY0FBYyxFQUFFLGVBQWUsQ0FBVSxDQUFDO0FBSWxLLE1BQU0sU0FBUztJQUNMLE1BQU0sQ0FBbUM7SUFDekMsV0FBVyxHQUFHLEtBQUssQ0FBQztJQUNwQixtQkFBbUIsR0FBRyxLQUFLLENBQUM7SUFFcEM7UUFDRSxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksV0FBVyxDQUFDLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUVNLElBQUk7UUFDVCxJQUFJLElBQUksQ0FBQyxXQUFXO1lBQUUsT0FBTztRQUM3QixJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDbkIsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ3RCLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1FBQzFCLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDO0lBQzFCLENBQUM7SUFFTyxXQUFXO1FBQ2pCLEtBQUssTUFBTSxJQUFJLElBQUksVUFBVSxFQUFFLENBQUM7WUFDOUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsSUFBSSxJQUFJLEVBQUUsRUFBRSxLQUFLLElBQUksRUFBRTtnQkFDcEMsSUFBSSxDQUFDLGVBQWUsQ0FB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
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@serve.zone/dcrouter",
3
3
  "private": false,
4
- "version": "8.0.0",
4
+ "version": "9.1.0",
5
5
  "description": "A multifaceted routing service handling mail and SMS delivery functions.",
6
6
  "type": "module",
7
7
  "exports": {
@@ -55,7 +55,7 @@
55
55
  "@push.rocks/smartrx": "^3.0.10",
56
56
  "@push.rocks/smartstate": "^2.0.30",
57
57
  "@push.rocks/smartunique": "^3.0.9",
58
- "@serve.zone/catalog": "^2.2.0",
58
+ "@serve.zone/catalog": "^2.5.0",
59
59
  "@serve.zone/interfaces": "^5.3.0",
60
60
  "@serve.zone/remoteingress": "^4.0.0",
61
61
  "@tsclass/tsclass": "^9.3.0",
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '8.0.0',
6
+ version: '9.1.0',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }
@@ -22,6 +22,7 @@ import { OpsServer } from './opsserver/index.js';
22
22
  import { MetricsManager } from './monitoring/index.js';
23
23
  import { RadiusServer, type IRadiusServerConfig } from './radius/index.js';
24
24
  import { RemoteIngressManager, TunnelManager } from './remoteingress/index.js';
25
+ import { RouteConfigManager, ApiTokenManager } from './config/index.js';
25
26
 
26
27
  export interface IDcRouterOptions {
27
28
  /** Base directory for all dcrouter data. Defaults to ~/.serve.zone/dcrouter */
@@ -212,6 +213,10 @@ export class DcRouter {
212
213
  public remoteIngressManager?: RemoteIngressManager;
213
214
  public tunnelManager?: TunnelManager;
214
215
 
216
+ // Programmatic config API
217
+ public routeConfigManager?: RouteConfigManager;
218
+ public apiTokenManager?: ApiTokenManager;
219
+
215
220
  // DNS query logging rate limiter state
216
221
  private dnsLogWindow: number[] = [];
217
222
  private dnsBatchCount: number = 0;
@@ -233,6 +238,9 @@ export class DcRouter {
233
238
  // TypedRouter for API endpoints
234
239
  public typedrouter = new plugins.typedrequest.TypedRouter();
235
240
 
241
+ // Cached constructor routes (computed once during setupSmartProxy, used by RouteConfigManager)
242
+ private constructorRoutes: plugins.smartproxy.IRouteConfig[] = [];
243
+
236
244
  // Environment access
237
245
  private qenv = new plugins.qenv.Qenv('./', '.nogit/');
238
246
 
@@ -275,7 +283,17 @@ export class DcRouter {
275
283
 
276
284
  // Set up SmartProxy for HTTP/HTTPS and all traffic including email routes
277
285
  await this.setupSmartProxy();
278
-
286
+
287
+ // Initialize programmatic config API managers
288
+ this.routeConfigManager = new RouteConfigManager(
289
+ this.storageManager,
290
+ () => this.getConstructorRoutes(),
291
+ () => this.smartProxy,
292
+ );
293
+ this.apiTokenManager = new ApiTokenManager(this.storageManager);
294
+ await this.apiTokenManager.initialize();
295
+ await this.routeConfigManager.initialize();
296
+
279
297
  // Set up unified email handling if configured
280
298
  if (this.options.emailConfig) {
281
299
  await this.setupUnifiedEmailHandling();
@@ -443,6 +461,9 @@ export class DcRouter {
443
461
  challengeHandlers.push(dns01Handler);
444
462
  }
445
463
 
464
+ // Cache constructor routes for RouteConfigManager
465
+ this.constructorRoutes = [...routes];
466
+
446
467
  // If we have routes or need a basic SmartProxy instance, create it
447
468
  if (routes.length > 0 || this.options.smartProxyConfig) {
448
469
  logger.log('info', 'Setting up SmartProxy with combined configuration');
@@ -857,6 +878,14 @@ export class DcRouter {
857
878
  return names;
858
879
  }
859
880
 
881
+ /**
882
+ * Get the routes derived from constructor config (smartProxy + email + DNS).
883
+ * Used by RouteConfigManager as the "hardcoded" base.
884
+ */
885
+ public getConstructorRoutes(): plugins.smartproxy.IRouteConfig[] {
886
+ return this.constructorRoutes;
887
+ }
888
+
860
889
  public async stop() {
861
890
  logger.log('info', 'Stopping DcRouter services...');
862
891
 
@@ -929,6 +958,8 @@ export class DcRouter {
929
958
  this.smartAcme = undefined;
930
959
  this.certProvisionScheduler = undefined;
931
960
  this.remoteIngressManager = undefined;
961
+ this.routeConfigManager = undefined;
962
+ this.apiTokenManager = undefined;
932
963
  this.certificateStatusMap.clear();
933
964
 
934
965
  logger.log('info', 'All DcRouter services stopped');
@@ -960,6 +991,11 @@ export class DcRouter {
960
991
  // Start new SmartProxy with updated configuration (will include email routes if configured)
961
992
  await this.setupSmartProxy();
962
993
 
994
+ // Re-apply programmatic routes and overrides after SmartProxy restart
995
+ if (this.routeConfigManager) {
996
+ await this.routeConfigManager.initialize();
997
+ }
998
+
963
999
  logger.log('info', 'SmartProxy configuration updated');
964
1000
  }
965
1001