@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.
- package/dist_serve/bundle.js +510 -517
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +3 -6
- package/dist_ts/classes.dcrouter.js +39 -53
- package/dist_ts/config/classes.reference-resolver.d.ts +8 -8
- package/dist_ts/config/classes.reference-resolver.js +6 -6
- package/dist_ts/config/classes.route-config-manager.d.ts +13 -13
- package/dist_ts/config/classes.route-config-manager.js +117 -144
- package/dist_ts/config/classes.target-profile-manager.d.ts +2 -2
- package/dist_ts/config/classes.target-profile-manager.js +7 -18
- package/dist_ts/db/documents/{classes.stored-route.doc.d.ts → classes.route.doc.d.ts} +6 -3
- package/dist_ts/db/documents/{classes.stored-route.doc.js → classes.route.doc.js} +21 -9
- package/dist_ts/db/documents/index.d.ts +1 -2
- package/dist_ts/db/documents/index.js +2 -3
- package/dist_ts/monitoring/classes.metricsmanager.js +86 -8
- package/dist_ts/opsserver/handlers/network-target.handler.js +3 -3
- package/dist_ts/opsserver/handlers/route-management.handler.js +3 -23
- package/dist_ts/opsserver/handlers/source-profile.handler.js +3 -3
- package/dist_ts_apiclient/classes.route.d.ts +2 -5
- package/dist_ts_apiclient/classes.route.js +13 -42
- package/dist_ts_interfaces/data/route-management.d.ts +8 -17
- package/dist_ts_interfaces/requests/route-management.d.ts +6 -37
- package/dist_ts_migrations/index.js +23 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +0 -5
- package/dist_ts_web/appstate.js +1 -38
- package/dist_ts_web/elements/network/ops-view-routes.js +60 -110
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +45 -55
- package/ts/config/classes.reference-resolver.ts +13 -13
- package/ts/config/classes.route-config-manager.ts +128 -146
- package/ts/config/classes.target-profile-manager.ts +7 -20
- package/ts/db/documents/{classes.stored-route.doc.ts → classes.route.doc.ts} +16 -5
- package/ts/db/documents/index.ts +1 -2
- package/ts/monitoring/classes.metricsmanager.ts +80 -7
- package/ts/opsserver/handlers/network-target.handler.ts +2 -2
- package/ts/opsserver/handlers/route-management.handler.ts +2 -34
- package/ts/opsserver/handlers/source-profile.handler.ts +2 -2
- package/ts_apiclient/classes.route.ts +12 -49
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +0 -52
- package/ts_web/elements/network/ops-view-routes.ts +65 -123
- package/dist_ts/db/documents/classes.route-override.doc.d.ts +0 -10
- package/dist_ts/db/documents/classes.route-override.doc.js +0 -91
- 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,
|
|
3
|
+
import { SourceProfileDoc, NetworkTargetDoc, RouteDoc } from '../db/index.js';
|
|
4
4
|
import type {
|
|
5
5
|
ISourceProfile,
|
|
6
6
|
INetworkTarget,
|
|
7
7
|
IRouteMetadata,
|
|
8
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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 {
|
|
3
|
+
import { RouteDoc } from '../db/index.js';
|
|
4
4
|
import type {
|
|
5
|
-
|
|
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
|
|
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
|
|
64
|
-
public
|
|
65
|
-
return this.
|
|
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
|
|
66
|
+
* Load persisted routes, seed config/email/dns routes, compute warnings, apply to SmartProxy.
|
|
70
67
|
*/
|
|
71
|
-
public async initialize(
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
//
|
|
83
|
+
// Route listing
|
|
81
84
|
// =========================================================================
|
|
82
85
|
|
|
83
86
|
public getMergedRoutes(): { routes: IMergedRoute[]; warnings: IRouteWarning[] } {
|
|
84
87
|
const merged: IMergedRoute[] = [];
|
|
85
88
|
|
|
86
|
-
|
|
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
|
-
|
|
93
|
-
enabled:
|
|
94
|
-
|
|
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
|
-
//
|
|
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 = `
|
|
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:
|
|
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.
|
|
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.
|
|
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.
|
|
205
|
-
this.
|
|
206
|
-
const doc = await
|
|
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
|
-
//
|
|
207
|
+
// Private: seed routes from constructor config
|
|
218
208
|
// =========================================================================
|
|
219
209
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
247
|
-
|
|
248
|
-
this.
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
|
261
|
-
const docs = await
|
|
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.
|
|
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.
|
|
276
|
-
logger.log('info', `Loaded ${this.
|
|
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:
|
|
298
|
-
const existingDoc = await
|
|
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
|
|
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
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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: '
|
|
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: `
|
|
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.
|
|
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
|
-
//
|
|
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
|
|
420
|
-
for (const route of this.
|
|
421
|
-
|
|
422
|
-
|
|
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
|
-
|
|
416
|
+
r = augmentRouteWithHttp3(r, { enabled: true, ...http3Config });
|
|
435
417
|
}
|
|
436
|
-
enabledRoutes.push(injectVpn(
|
|
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
|
|
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.
|
|
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 {
|
|
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:
|
|
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
|
|
249
|
-
for (const route of allRoutes) {
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
|
|
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
|
|
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<
|
|
37
|
-
return await
|
|
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
|
|
41
|
-
return await
|
|
51
|
+
public static async findByOrigin(origin: 'config' | 'email' | 'dns' | 'api'): Promise<RouteDoc[]> {
|
|
52
|
+
return await RouteDoc.getInstances({ origin });
|
|
42
53
|
}
|
|
43
54
|
}
|
package/ts/db/documents/index.ts
CHANGED
|
@@ -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.
|
|
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';
|