@serve.zone/dcrouter 13.15.0 → 13.16.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 (46) hide show
  1. package/dist_serve/bundle.js +510 -517
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/classes.dcrouter.d.ts +3 -6
  4. package/dist_ts/classes.dcrouter.js +39 -53
  5. package/dist_ts/config/classes.reference-resolver.d.ts +8 -8
  6. package/dist_ts/config/classes.reference-resolver.js +6 -6
  7. package/dist_ts/config/classes.route-config-manager.d.ts +13 -13
  8. package/dist_ts/config/classes.route-config-manager.js +117 -144
  9. package/dist_ts/config/classes.target-profile-manager.d.ts +2 -2
  10. package/dist_ts/config/classes.target-profile-manager.js +7 -18
  11. package/dist_ts/db/documents/{classes.stored-route.doc.d.ts → classes.route.doc.d.ts} +6 -3
  12. package/dist_ts/db/documents/{classes.stored-route.doc.js → classes.route.doc.js} +21 -9
  13. package/dist_ts/db/documents/index.d.ts +1 -2
  14. package/dist_ts/db/documents/index.js +2 -3
  15. package/dist_ts/monitoring/classes.metricsmanager.js +86 -8
  16. package/dist_ts/opsserver/handlers/network-target.handler.js +3 -3
  17. package/dist_ts/opsserver/handlers/route-management.handler.js +3 -23
  18. package/dist_ts/opsserver/handlers/source-profile.handler.js +3 -3
  19. package/dist_ts_apiclient/classes.route.d.ts +2 -5
  20. package/dist_ts_apiclient/classes.route.js +13 -42
  21. package/dist_ts_interfaces/data/route-management.d.ts +8 -17
  22. package/dist_ts_interfaces/requests/route-management.d.ts +6 -37
  23. package/dist_ts_migrations/index.js +23 -1
  24. package/dist_ts_web/00_commitinfo_data.js +1 -1
  25. package/dist_ts_web/appstate.d.ts +0 -5
  26. package/dist_ts_web/appstate.js +1 -38
  27. package/dist_ts_web/elements/network/ops-view-routes.js +60 -110
  28. package/package.json +1 -1
  29. package/ts/00_commitinfo_data.ts +1 -1
  30. package/ts/classes.dcrouter.ts +45 -55
  31. package/ts/config/classes.reference-resolver.ts +13 -13
  32. package/ts/config/classes.route-config-manager.ts +128 -146
  33. package/ts/config/classes.target-profile-manager.ts +7 -20
  34. package/ts/db/documents/{classes.stored-route.doc.ts → classes.route.doc.ts} +16 -5
  35. package/ts/db/documents/index.ts +1 -2
  36. package/ts/monitoring/classes.metricsmanager.ts +80 -7
  37. package/ts/opsserver/handlers/network-target.handler.ts +2 -2
  38. package/ts/opsserver/handlers/route-management.handler.ts +2 -34
  39. package/ts/opsserver/handlers/source-profile.handler.ts +2 -2
  40. package/ts_apiclient/classes.route.ts +12 -49
  41. package/ts_web/00_commitinfo_data.ts +1 -1
  42. package/ts_web/appstate.ts +0 -52
  43. package/ts_web/elements/network/ops-view-routes.ts +65 -123
  44. package/dist_ts/db/documents/classes.route-override.doc.d.ts +0 -10
  45. package/dist_ts/db/documents/classes.route-override.doc.js +0 -91
  46. package/ts/db/documents/classes.route-override.doc.ts +0 -32
@@ -1,11 +1,11 @@
1
1
  import * as plugins from '../plugins.js';
2
2
  import { logger } from '../logger.js';
3
- import { SourceProfileDoc, NetworkTargetDoc, StoredRouteDoc } from '../db/index.js';
3
+ import { SourceProfileDoc, NetworkTargetDoc, RouteDoc } from '../db/index.js';
4
4
  import type {
5
5
  ISourceProfile,
6
6
  INetworkTarget,
7
7
  IRouteMetadata,
8
- IStoredRoute,
8
+ IRoute,
9
9
  IRouteSecurity,
10
10
  } from '../../ts_interfaces/data/route-management.js';
11
11
 
@@ -81,7 +81,7 @@ export class ReferenceResolver {
81
81
  public async deleteProfile(
82
82
  id: string,
83
83
  force: boolean,
84
- storedRoutes?: Map<string, IStoredRoute>,
84
+ storedRoutes?: Map<string, IRoute>,
85
85
  ): Promise<{ success: boolean; message?: string }> {
86
86
  const profile = this.profiles.get(id);
87
87
  if (!profile) {
@@ -131,7 +131,7 @@ export class ReferenceResolver {
131
131
  return [...this.profiles.values()];
132
132
  }
133
133
 
134
- public getProfileUsage(storedRoutes: Map<string, IStoredRoute>): Map<string, Array<{ id: string; routeName: string }>> {
134
+ public getProfileUsage(storedRoutes: Map<string, IRoute>): Map<string, Array<{ id: string; routeName: string }>> {
135
135
  const usage = new Map<string, Array<{ id: string; routeName: string }>>();
136
136
  for (const profile of this.profiles.values()) {
137
137
  usage.set(profile.id, []);
@@ -147,7 +147,7 @@ export class ReferenceResolver {
147
147
 
148
148
  public getProfileUsageForId(
149
149
  profileId: string,
150
- storedRoutes: Map<string, IStoredRoute>,
150
+ storedRoutes: Map<string, IRoute>,
151
151
  ): Array<{ id: string; routeName: string }> {
152
152
  const routes: Array<{ id: string; routeName: string }> = [];
153
153
  for (const [routeId, stored] of storedRoutes) {
@@ -214,7 +214,7 @@ export class ReferenceResolver {
214
214
  public async deleteTarget(
215
215
  id: string,
216
216
  force: boolean,
217
- storedRoutes?: Map<string, IStoredRoute>,
217
+ storedRoutes?: Map<string, IRoute>,
218
218
  ): Promise<{ success: boolean; message?: string }> {
219
219
  const target = this.targets.get(id);
220
220
  if (!target) {
@@ -263,7 +263,7 @@ export class ReferenceResolver {
263
263
 
264
264
  public getTargetUsageForId(
265
265
  targetId: string,
266
- storedRoutes: Map<string, IStoredRoute>,
266
+ storedRoutes: Map<string, IRoute>,
267
267
  ): Array<{ id: string; routeName: string }> {
268
268
  const routes: Array<{ id: string; routeName: string }> = [];
269
269
  for (const [routeId, stored] of storedRoutes) {
@@ -334,20 +334,20 @@ export class ReferenceResolver {
334
334
  // =========================================================================
335
335
 
336
336
  public async findRoutesByProfileRef(profileId: string): Promise<string[]> {
337
- const docs = await StoredRouteDoc.findAll();
337
+ const docs = await RouteDoc.findAll();
338
338
  return docs
339
339
  .filter((doc) => doc.metadata?.sourceProfileRef === profileId)
340
340
  .map((doc) => doc.id);
341
341
  }
342
342
 
343
343
  public async findRoutesByTargetRef(targetId: string): Promise<string[]> {
344
- const docs = await StoredRouteDoc.findAll();
344
+ const docs = await RouteDoc.findAll();
345
345
  return docs
346
346
  .filter((doc) => doc.metadata?.networkTargetRef === targetId)
347
347
  .map((doc) => doc.id);
348
348
  }
349
349
 
350
- public findRoutesByProfileRefSync(profileId: string, storedRoutes: Map<string, IStoredRoute>): string[] {
350
+ public findRoutesByProfileRefSync(profileId: string, storedRoutes: Map<string, IRoute>): string[] {
351
351
  const ids: string[] = [];
352
352
  for (const [routeId, stored] of storedRoutes) {
353
353
  if (stored.metadata?.sourceProfileRef === profileId) {
@@ -357,7 +357,7 @@ export class ReferenceResolver {
357
357
  return ids;
358
358
  }
359
359
 
360
- public findRoutesByTargetRefSync(targetId: string, storedRoutes: Map<string, IStoredRoute>): string[] {
360
+ public findRoutesByTargetRefSync(targetId: string, storedRoutes: Map<string, IRoute>): string[] {
361
361
  const ids: string[] = [];
362
362
  for (const [routeId, stored] of storedRoutes) {
363
363
  if (stored.metadata?.networkTargetRef === targetId) {
@@ -547,7 +547,7 @@ export class ReferenceResolver {
547
547
 
548
548
  private async clearProfileRefsOnRoutes(routeIds: string[]): Promise<void> {
549
549
  for (const routeId of routeIds) {
550
- const doc = await StoredRouteDoc.findById(routeId);
550
+ const doc = await RouteDoc.findById(routeId);
551
551
  if (doc?.metadata) {
552
552
  doc.metadata = {
553
553
  ...doc.metadata,
@@ -562,7 +562,7 @@ export class ReferenceResolver {
562
562
 
563
563
  private async clearTargetRefsOnRoutes(routeIds: string[]): Promise<void> {
564
564
  for (const routeId of routeIds) {
565
- const doc = await StoredRouteDoc.findById(routeId);
565
+ const doc = await RouteDoc.findById(routeId);
566
566
  if (doc?.metadata) {
567
567
  doc.metadata = {
568
568
  ...doc.metadata,
@@ -1,9 +1,8 @@
1
1
  import * as plugins from '../plugins.js';
2
2
  import { logger } from '../logger.js';
3
- import { StoredRouteDoc, RouteOverrideDoc } from '../db/index.js';
3
+ import { RouteDoc } from '../db/index.js';
4
4
  import type {
5
- IStoredRoute,
6
- IRouteOverride,
5
+ IRoute,
7
6
  IMergedRoute,
8
7
  IRouteWarning,
9
8
  IRouteMetadata,
@@ -46,13 +45,11 @@ class RouteUpdateMutex {
46
45
  }
47
46
 
48
47
  export class RouteConfigManager {
49
- private storedRoutes = new Map<string, IStoredRoute>();
50
- private overrides = new Map<string, IRouteOverride>();
48
+ private routes = new Map<string, IRoute>();
51
49
  private warnings: IRouteWarning[] = [];
52
50
  private routeUpdateMutex = new RouteUpdateMutex();
53
51
 
54
52
  constructor(
55
- private getHardcodedRoutes: () => plugins.smartproxy.IRouteConfig[],
56
53
  private getSmartProxy: () => plugins.smartproxy.SmartProxy | undefined,
57
54
  private getHttp3Config?: () => IHttp3Config | undefined,
58
55
  private getVpnClientIpsForRoute?: (route: IDcRouterRouteConfig, routeId?: string) => TIpAllowEntry[],
@@ -60,52 +57,44 @@ export class RouteConfigManager {
60
57
  private onRoutesApplied?: (routes: plugins.smartproxy.IRouteConfig[]) => void,
61
58
  ) {}
62
59
 
63
- /** Expose stored routes map for reference resolution lookups. */
64
- public getStoredRoutes(): Map<string, IStoredRoute> {
65
- return this.storedRoutes;
60
+ /** Expose routes map for reference resolution lookups. */
61
+ public getRoutes(): Map<string, IRoute> {
62
+ return this.routes;
66
63
  }
67
64
 
68
65
  /**
69
- * Load persisted routes and overrides, compute warnings, apply to SmartProxy.
66
+ * Load persisted routes, seed config/email/dns routes, compute warnings, apply to SmartProxy.
70
67
  */
71
- public async initialize(): Promise<void> {
72
- await this.loadStoredRoutes();
73
- await this.loadOverrides();
68
+ public async initialize(
69
+ configRoutes: IDcRouterRouteConfig[] = [],
70
+ emailRoutes: IDcRouterRouteConfig[] = [],
71
+ dnsRoutes: IDcRouterRouteConfig[] = [],
72
+ ): Promise<void> {
73
+ await this.loadRoutes();
74
+ await this.seedRoutes(configRoutes, 'config');
75
+ await this.seedRoutes(emailRoutes, 'email');
76
+ await this.seedRoutes(dnsRoutes, 'dns');
74
77
  this.computeWarnings();
75
78
  this.logWarnings();
76
79
  await this.applyRoutes();
77
80
  }
78
81
 
79
82
  // =========================================================================
80
- // Merged view
83
+ // Route listing
81
84
  // =========================================================================
82
85
 
83
86
  public getMergedRoutes(): { routes: IMergedRoute[]; warnings: IRouteWarning[] } {
84
87
  const merged: IMergedRoute[] = [];
85
88
 
86
- // Hardcoded routes
87
- for (const route of this.getHardcodedRoutes()) {
88
- const name = route.name || '';
89
- const override = this.overrides.get(name);
89
+ for (const route of this.routes.values()) {
90
90
  merged.push({
91
- route,
92
- source: 'hardcoded',
93
- enabled: override ? override.enabled : true,
94
- overridden: !!override,
95
- });
96
- }
97
-
98
- // Programmatic routes
99
- for (const stored of this.storedRoutes.values()) {
100
- merged.push({
101
- route: stored.route,
102
- source: 'programmatic',
103
- enabled: stored.enabled,
104
- overridden: false,
105
- storedRouteId: stored.id,
106
- createdAt: stored.createdAt,
107
- updatedAt: stored.updatedAt,
108
- metadata: stored.metadata,
91
+ route: route.route,
92
+ id: route.id,
93
+ enabled: route.enabled,
94
+ origin: route.origin,
95
+ createdAt: route.createdAt,
96
+ updatedAt: route.updatedAt,
97
+ metadata: route.metadata,
109
98
  });
110
99
  }
111
100
 
@@ -113,7 +102,7 @@ export class RouteConfigManager {
113
102
  }
114
103
 
115
104
  // =========================================================================
116
- // Programmatic route CRUD
105
+ // Route CRUD
117
106
  // =========================================================================
118
107
 
119
108
  public async createRoute(
@@ -127,7 +116,7 @@ export class RouteConfigManager {
127
116
 
128
117
  // Ensure route has a name
129
118
  if (!route.name) {
130
- route.name = `programmatic-${id.slice(0, 8)}`;
119
+ route.name = `route-${id.slice(0, 8)}`;
131
120
  }
132
121
 
133
122
  // Resolve references if metadata has refs and resolver is available
@@ -138,17 +127,18 @@ export class RouteConfigManager {
138
127
  resolvedMetadata = resolved.metadata;
139
128
  }
140
129
 
141
- const stored: IStoredRoute = {
130
+ const stored: IRoute = {
142
131
  id,
143
132
  route,
144
133
  enabled,
145
134
  createdAt: now,
146
135
  updatedAt: now,
147
136
  createdBy,
137
+ origin: 'api',
148
138
  metadata: resolvedMetadata,
149
139
  };
150
140
 
151
- this.storedRoutes.set(id, stored);
141
+ this.routes.set(id, stored);
152
142
  await this.persistRoute(stored);
153
143
  await this.applyRoutes();
154
144
  return id;
@@ -162,7 +152,7 @@ export class RouteConfigManager {
162
152
  metadata?: Partial<IRouteMetadata>;
163
153
  },
164
154
  ): Promise<boolean> {
165
- const stored = this.storedRoutes.get(id);
155
+ const stored = this.routes.get(id);
166
156
  if (!stored) return false;
167
157
 
168
158
  if (patch.route) {
@@ -201,9 +191,9 @@ export class RouteConfigManager {
201
191
  }
202
192
 
203
193
  public async deleteRoute(id: string): Promise<boolean> {
204
- if (!this.storedRoutes.has(id)) return false;
205
- this.storedRoutes.delete(id);
206
- const doc = await StoredRouteDoc.findById(id);
194
+ if (!this.routes.has(id)) return false;
195
+ this.routes.delete(id);
196
+ const doc = await RouteDoc.findById(id);
207
197
  if (doc) await doc.delete();
208
198
  await this.applyRoutes();
209
199
  return true;
@@ -214,103 +204,124 @@ export class RouteConfigManager {
214
204
  }
215
205
 
216
206
  // =========================================================================
217
- // Hardcoded route overrides
207
+ // Private: seed routes from constructor config
218
208
  // =========================================================================
219
209
 
220
- public async setOverride(routeName: string, enabled: boolean, updatedBy: string): Promise<void> {
221
- const override: IRouteOverride = {
222
- routeName,
223
- enabled,
224
- updatedAt: Date.now(),
225
- updatedBy,
226
- };
227
- this.overrides.set(routeName, override);
228
- const existingDoc = await RouteOverrideDoc.findByRouteName(routeName);
229
- if (existingDoc) {
230
- existingDoc.enabled = override.enabled;
231
- existingDoc.updatedAt = override.updatedAt;
232
- existingDoc.updatedBy = override.updatedBy;
233
- await existingDoc.save();
234
- } else {
235
- const doc = new RouteOverrideDoc();
236
- doc.routeName = override.routeName;
237
- doc.enabled = override.enabled;
238
- doc.updatedAt = override.updatedAt;
239
- doc.updatedBy = override.updatedBy;
240
- await doc.save();
210
+ /**
211
+ * Upsert seed routes by name+origin. Preserves user's `enabled` state.
212
+ * Deletes stale DB routes whose origin matches but name is not in the seed set.
213
+ */
214
+ private async seedRoutes(
215
+ seedRoutes: IDcRouterRouteConfig[],
216
+ origin: 'config' | 'email' | 'dns',
217
+ ): Promise<void> {
218
+ if (seedRoutes.length === 0) return;
219
+
220
+ const seedNames = new Set<string>();
221
+ let seeded = 0;
222
+ let updated = 0;
223
+
224
+ for (const route of seedRoutes) {
225
+ const name = route.name || '';
226
+ seedNames.add(name);
227
+
228
+ // Check if a route with this name+origin already exists in memory
229
+ let existingId: string | undefined;
230
+ for (const [id, r] of this.routes) {
231
+ if (r.origin === origin && r.route.name === name) {
232
+ existingId = id;
233
+ break;
234
+ }
235
+ }
236
+
237
+ if (existingId) {
238
+ // Update route config but preserve enabled state
239
+ const existing = this.routes.get(existingId)!;
240
+ existing.route = route;
241
+ existing.updatedAt = Date.now();
242
+ await this.persistRoute(existing);
243
+ updated++;
244
+ } else {
245
+ // Insert new seed route
246
+ const id = plugins.uuid.v4();
247
+ const now = Date.now();
248
+ const newRoute: IRoute = {
249
+ id,
250
+ route,
251
+ enabled: true,
252
+ createdAt: now,
253
+ updatedAt: now,
254
+ createdBy: 'system',
255
+ origin,
256
+ };
257
+ this.routes.set(id, newRoute);
258
+ await this.persistRoute(newRoute);
259
+ seeded++;
260
+ }
241
261
  }
242
- this.computeWarnings();
243
- await this.applyRoutes();
244
- }
245
262
 
246
- public async removeOverride(routeName: string): Promise<boolean> {
247
- if (!this.overrides.has(routeName)) return false;
248
- this.overrides.delete(routeName);
249
- const doc = await RouteOverrideDoc.findByRouteName(routeName);
250
- if (doc) await doc.delete();
251
- this.computeWarnings();
252
- await this.applyRoutes();
253
- return true;
263
+ // Delete stale routes: same origin but name not in current seed set
264
+ const staleIds: string[] = [];
265
+ for (const [id, r] of this.routes) {
266
+ if (r.origin === origin && !seedNames.has(r.route.name || '')) {
267
+ staleIds.push(id);
268
+ }
269
+ }
270
+ for (const id of staleIds) {
271
+ this.routes.delete(id);
272
+ const doc = await RouteDoc.findById(id);
273
+ if (doc) await doc.delete();
274
+ }
275
+
276
+ if (seeded > 0 || updated > 0 || staleIds.length > 0) {
277
+ logger.log('info', `Seed routes (${origin}): ${seeded} new, ${updated} updated, ${staleIds.length} stale removed`);
278
+ }
254
279
  }
255
280
 
256
281
  // =========================================================================
257
282
  // Private: persistence
258
283
  // =========================================================================
259
284
 
260
- private async loadStoredRoutes(): Promise<void> {
261
- const docs = await StoredRouteDoc.findAll();
285
+ private async loadRoutes(): Promise<void> {
286
+ const docs = await RouteDoc.findAll();
262
287
  for (const doc of docs) {
263
288
  if (doc.id) {
264
- this.storedRoutes.set(doc.id, {
289
+ this.routes.set(doc.id, {
265
290
  id: doc.id,
266
291
  route: doc.route,
267
292
  enabled: doc.enabled,
268
293
  createdAt: doc.createdAt,
269
294
  updatedAt: doc.updatedAt,
270
295
  createdBy: doc.createdBy,
296
+ origin: doc.origin || 'api',
271
297
  metadata: doc.metadata,
272
298
  });
273
299
  }
274
300
  }
275
- if (this.storedRoutes.size > 0) {
276
- logger.log('info', `Loaded ${this.storedRoutes.size} programmatic route(s) from storage`);
277
- }
278
- }
279
-
280
- private async loadOverrides(): Promise<void> {
281
- const docs = await RouteOverrideDoc.findAll();
282
- for (const doc of docs) {
283
- if (doc.routeName) {
284
- this.overrides.set(doc.routeName, {
285
- routeName: doc.routeName,
286
- enabled: doc.enabled,
287
- updatedAt: doc.updatedAt,
288
- updatedBy: doc.updatedBy,
289
- });
290
- }
291
- }
292
- if (this.overrides.size > 0) {
293
- logger.log('info', `Loaded ${this.overrides.size} route override(s) from storage`);
301
+ if (this.routes.size > 0) {
302
+ logger.log('info', `Loaded ${this.routes.size} route(s) from database`);
294
303
  }
295
304
  }
296
305
 
297
- private async persistRoute(stored: IStoredRoute): Promise<void> {
298
- const existingDoc = await StoredRouteDoc.findById(stored.id);
306
+ private async persistRoute(stored: IRoute): Promise<void> {
307
+ const existingDoc = await RouteDoc.findById(stored.id);
299
308
  if (existingDoc) {
300
309
  existingDoc.route = stored.route;
301
310
  existingDoc.enabled = stored.enabled;
302
311
  existingDoc.updatedAt = stored.updatedAt;
303
312
  existingDoc.createdBy = stored.createdBy;
313
+ existingDoc.origin = stored.origin;
304
314
  existingDoc.metadata = stored.metadata;
305
315
  await existingDoc.save();
306
316
  } else {
307
- const doc = new StoredRouteDoc();
317
+ const doc = new RouteDoc();
308
318
  doc.id = stored.id;
309
319
  doc.route = stored.route;
310
320
  doc.enabled = stored.enabled;
311
321
  doc.createdAt = stored.createdAt;
312
322
  doc.updatedAt = stored.updatedAt;
313
323
  doc.createdBy = stored.createdBy;
324
+ doc.origin = stored.origin;
314
325
  doc.metadata = stored.metadata;
315
326
  await doc.save();
316
327
  }
@@ -322,33 +333,14 @@ export class RouteConfigManager {
322
333
 
323
334
  private computeWarnings(): void {
324
335
  this.warnings = [];
325
- const hardcodedNames = new Set(this.getHardcodedRoutes().map((r) => r.name || ''));
326
336
 
327
- // Check overrides
328
- for (const [routeName, override] of this.overrides) {
329
- if (!hardcodedNames.has(routeName)) {
337
+ for (const route of this.routes.values()) {
338
+ if (!route.enabled) {
339
+ const name = route.route.name || route.id;
330
340
  this.warnings.push({
331
- type: 'orphaned-override',
332
- routeName,
333
- message: `Orphaned override for route '${routeName}' — hardcoded route no longer exists`,
334
- });
335
- } else if (!override.enabled) {
336
- this.warnings.push({
337
- type: 'disabled-hardcoded',
338
- routeName,
339
- message: `Route '${routeName}' is disabled via API override`,
340
- });
341
- }
342
- }
343
-
344
- // Check disabled programmatic routes
345
- for (const stored of this.storedRoutes.values()) {
346
- if (!stored.enabled) {
347
- const name = stored.route.name || stored.id;
348
- this.warnings.push({
349
- type: 'disabled-programmatic',
341
+ type: 'disabled-route',
350
342
  routeName: name,
351
- message: `Programmatic route '${name}' (id: ${stored.id}) is disabled`,
343
+ message: `Route '${name}' (id: ${route.id}) is disabled`,
352
344
  });
353
345
  }
354
346
  }
@@ -372,7 +364,7 @@ export class RouteConfigManager {
372
364
  if (!this.referenceResolver || routeIds.length === 0) return;
373
365
 
374
366
  for (const routeId of routeIds) {
375
- const stored = this.storedRoutes.get(routeId);
367
+ const stored = this.routes.get(routeId);
376
368
  if (!stored?.metadata) continue;
377
369
 
378
370
  const resolved = this.referenceResolver.resolveRoute(stored.route, stored.metadata);
@@ -387,7 +379,7 @@ export class RouteConfigManager {
387
379
  }
388
380
 
389
381
  // =========================================================================
390
- // Private: apply merged routes to SmartProxy
382
+ // Apply routes to SmartProxy
391
383
  // =========================================================================
392
384
 
393
385
  public async applyRoutes(): Promise<void> {
@@ -416,35 +408,25 @@ export class RouteConfigManager {
416
408
  };
417
409
  };
418
410
 
419
- // Add enabled hardcoded routes (respecting overrides, with fresh VPN injection)
420
- for (const route of this.getHardcodedRoutes()) {
421
- const name = route.name || '';
422
- const override = this.overrides.get(name);
423
- if (override && !override.enabled) {
424
- continue; // Skip disabled hardcoded route
425
- }
426
- enabledRoutes.push(injectVpn(route));
427
- }
428
-
429
- // Add enabled programmatic routes (with HTTP/3 and VPN augmentation)
430
- for (const stored of this.storedRoutes.values()) {
431
- if (stored.enabled) {
432
- let route = stored.route;
411
+ // Add all enabled routes with HTTP/3 and VPN augmentation
412
+ for (const route of this.routes.values()) {
413
+ if (route.enabled) {
414
+ let r = route.route;
433
415
  if (http3Config?.enabled !== false) {
434
- route = augmentRouteWithHttp3(route, { enabled: true, ...http3Config });
416
+ r = augmentRouteWithHttp3(r, { enabled: true, ...http3Config });
435
417
  }
436
- enabledRoutes.push(injectVpn(route, stored.id));
418
+ enabledRoutes.push(injectVpn(r, route.id));
437
419
  }
438
420
  }
439
421
 
440
422
  await smartProxy.updateRoutes(enabledRoutes);
441
423
 
442
- // Notify listeners (e.g. RemoteIngressManager) of the merged route set
424
+ // Notify listeners (e.g. RemoteIngressManager) of the route set
443
425
  if (this.onRoutesApplied) {
444
426
  this.onRoutesApplied(enabledRoutes);
445
427
  }
446
428
 
447
- logger.log('info', `Applied ${enabledRoutes.length} routes to SmartProxy (${this.storedRoutes.size} programmatic, ${this.overrides.size} overrides)`);
429
+ logger.log('info', `Applied ${enabledRoutes.length} routes to SmartProxy (${this.routes.size} total)`);
448
430
  });
449
431
  }
450
432
  }
@@ -3,7 +3,7 @@ import { logger } from '../logger.js';
3
3
  import { TargetProfileDoc, VpnClientDoc } from '../db/index.js';
4
4
  import type { ITargetProfile, ITargetProfileTarget } from '../../ts_interfaces/data/target-profile.js';
5
5
  import type { IDcRouterRouteConfig } from '../../ts_interfaces/data/remoteingress.js';
6
- import type { IStoredRoute } from '../../ts_interfaces/data/route-management.js';
6
+ import type { IRoute } from '../../ts_interfaces/data/route-management.js';
7
7
 
8
8
  /**
9
9
  * Manages TargetProfiles (target-side: what can be accessed).
@@ -220,8 +220,7 @@ export class TargetProfileManager {
220
220
  */
221
221
  public getClientAccessSpec(
222
222
  targetProfileIds: string[],
223
- allRoutes: IDcRouterRouteConfig[],
224
- storedRoutes: Map<string, IStoredRoute>,
223
+ allRoutes: Map<string, IRoute>,
225
224
  ): { domains: string[]; targetIps: string[] } {
226
225
  const domains = new Set<string>();
227
226
  const targetIps = new Set<string>();
@@ -245,23 +244,11 @@ export class TargetProfileManager {
245
244
  }
246
245
  }
247
246
 
248
- // Route references: scan constructor routes
249
- for (const route of allRoutes) {
250
- if (this.routeMatchesProfile(route as IDcRouterRouteConfig, undefined, profile)) {
251
- const routeDomains = (route.match as any)?.domains;
252
- if (Array.isArray(routeDomains)) {
253
- for (const d of routeDomains) {
254
- domains.add(d);
255
- }
256
- }
257
- }
258
- }
259
-
260
- // Route references: scan stored routes
261
- for (const [storedId, stored] of storedRoutes) {
262
- if (!stored.enabled) continue;
263
- if (this.routeMatchesProfile(stored.route as IDcRouterRouteConfig, storedId, profile)) {
264
- const routeDomains = (stored.route.match as any)?.domains;
247
+ // Route references: scan all routes
248
+ for (const [routeId, route] of allRoutes) {
249
+ if (!route.enabled) continue;
250
+ if (this.routeMatchesProfile(route.route as IDcRouterRouteConfig, routeId, profile)) {
251
+ const routeDomains = (route.route.match as any)?.domains;
265
252
  if (Array.isArray(routeDomains)) {
266
253
  for (const d of routeDomains) {
267
254
  domains.add(d);
@@ -6,7 +6,7 @@ import type { IDcRouterRouteConfig } from '../../../ts_interfaces/data/remoteing
6
6
  const getDb = () => DcRouterDb.getInstance().getDb();
7
7
 
8
8
  @plugins.smartdata.Collection(() => getDb())
9
- export class StoredRouteDoc extends plugins.smartdata.SmartDataDbDoc<StoredRouteDoc, StoredRouteDoc> {
9
+ export class RouteDoc extends plugins.smartdata.SmartDataDbDoc<RouteDoc, RouteDoc> {
10
10
  @plugins.smartdata.unI()
11
11
  @plugins.smartdata.svDb()
12
12
  public id!: string;
@@ -26,6 +26,9 @@ export class StoredRouteDoc extends plugins.smartdata.SmartDataDbDoc<StoredRoute
26
26
  @plugins.smartdata.svDb()
27
27
  public createdBy!: string;
28
28
 
29
+ @plugins.smartdata.svDb()
30
+ public origin!: 'config' | 'email' | 'dns' | 'api';
31
+
29
32
  @plugins.smartdata.svDb()
30
33
  public metadata?: IRouteMetadata;
31
34
 
@@ -33,11 +36,19 @@ export class StoredRouteDoc extends plugins.smartdata.SmartDataDbDoc<StoredRoute
33
36
  super();
34
37
  }
35
38
 
36
- public static async findById(id: string): Promise<StoredRouteDoc | null> {
37
- return await StoredRouteDoc.getInstance({ id });
39
+ public static async findById(id: string): Promise<RouteDoc | null> {
40
+ return await RouteDoc.getInstance({ id });
41
+ }
42
+
43
+ public static async findAll(): Promise<RouteDoc[]> {
44
+ return await RouteDoc.getInstances({});
45
+ }
46
+
47
+ public static async findByName(name: string): Promise<RouteDoc | null> {
48
+ return await RouteDoc.getInstance({ 'route.name': name });
38
49
  }
39
50
 
40
- public static async findAll(): Promise<StoredRouteDoc[]> {
41
- return await StoredRouteDoc.getInstances({});
51
+ public static async findByOrigin(origin: 'config' | 'email' | 'dns' | 'api'): Promise<RouteDoc[]> {
52
+ return await RouteDoc.getInstances({ origin });
42
53
  }
43
54
  }
@@ -3,8 +3,7 @@ export * from './classes.cached.email.js';
3
3
  export * from './classes.cached.ip.reputation.js';
4
4
 
5
5
  // Config document classes
6
- export * from './classes.stored-route.doc.js';
7
- export * from './classes.route-override.doc.js';
6
+ export * from './classes.route.doc.js';
8
7
  export * from './classes.api-token.doc.js';
9
8
  export * from './classes.source-profile.doc.js';
10
9
  export * from './classes.target-profile.doc.js';