feat(config): add reusable security profiles and network targets with route reference resolution

This commit is contained in:
2026-04-02 15:44:36 +00:00
parent 6344c2deae
commit 55699f6618
31 changed files with 2845 additions and 12 deletions

View File

@@ -6,9 +6,11 @@ import type {
IRouteOverride,
IMergedRoute,
IRouteWarning,
IRouteMetadata,
} from '../../ts_interfaces/data/route-management.js';
import type { IDcRouterRouteConfig } from '../../ts_interfaces/data/remoteingress.js';
import { type IHttp3Config, augmentRouteWithHttp3 } from '../http3/index.js';
import type { ReferenceResolver } from './classes.reference-resolver.js';
export class RouteConfigManager {
private storedRoutes = new Map<string, IStoredRoute>();
@@ -20,8 +22,14 @@ export class RouteConfigManager {
private getSmartProxy: () => plugins.smartproxy.SmartProxy | undefined,
private getHttp3Config?: () => IHttp3Config | undefined,
private getVpnAllowList?: (tags?: string[]) => string[],
private referenceResolver?: ReferenceResolver,
) {}
/** Expose stored routes map for reference resolution lookups. */
public getStoredRoutes(): Map<string, IStoredRoute> {
return this.storedRoutes;
}
/**
* Load persisted routes and overrides, compute warnings, apply to SmartProxy.
*/
@@ -62,6 +70,7 @@ export class RouteConfigManager {
storedRouteId: stored.id,
createdAt: stored.createdAt,
updatedAt: stored.updatedAt,
metadata: stored.metadata,
});
}
@@ -76,6 +85,7 @@ export class RouteConfigManager {
route: plugins.smartproxy.IRouteConfig,
createdBy: string,
enabled = true,
metadata?: IRouteMetadata,
): Promise<string> {
const id = plugins.uuid.v4();
const now = Date.now();
@@ -85,6 +95,14 @@ export class RouteConfigManager {
route.name = `programmatic-${id.slice(0, 8)}`;
}
// Resolve references if metadata has refs and resolver is available
let resolvedMetadata = metadata;
if (metadata && this.referenceResolver) {
const resolved = this.referenceResolver.resolveRoute(route, metadata);
route = resolved.route;
resolvedMetadata = resolved.metadata;
}
const stored: IStoredRoute = {
id,
route,
@@ -92,6 +110,7 @@ export class RouteConfigManager {
createdAt: now,
updatedAt: now,
createdBy,
metadata: resolvedMetadata,
};
this.storedRoutes.set(id, stored);
@@ -102,7 +121,11 @@ export class RouteConfigManager {
public async updateRoute(
id: string,
patch: { route?: Partial<plugins.smartproxy.IRouteConfig>; enabled?: boolean },
patch: {
route?: Partial<plugins.smartproxy.IRouteConfig>;
enabled?: boolean;
metadata?: Partial<IRouteMetadata>;
},
): Promise<boolean> {
const stored = this.storedRoutes.get(id);
if (!stored) return false;
@@ -113,6 +136,17 @@ export class RouteConfigManager {
if (patch.enabled !== undefined) {
stored.enabled = patch.enabled;
}
if (patch.metadata !== undefined) {
stored.metadata = { ...stored.metadata, ...patch.metadata };
}
// Re-resolve if metadata refs exist and resolver is available
if (stored.metadata && this.referenceResolver) {
const resolved = this.referenceResolver.resolveRoute(stored.route, stored.metadata);
stored.route = resolved.route;
stored.metadata = resolved.metadata;
}
stored.updatedAt = Date.now();
await this.persistRoute(stored);
@@ -188,6 +222,7 @@ export class RouteConfigManager {
createdAt: doc.createdAt,
updatedAt: doc.updatedAt,
createdBy: doc.createdBy,
metadata: doc.metadata,
});
}
}
@@ -220,6 +255,7 @@ export class RouteConfigManager {
existingDoc.enabled = stored.enabled;
existingDoc.updatedAt = stored.updatedAt;
existingDoc.createdBy = stored.createdBy;
existingDoc.metadata = stored.metadata;
await existingDoc.save();
} else {
const doc = new StoredRouteDoc();
@@ -229,6 +265,7 @@ export class RouteConfigManager {
doc.createdAt = stored.createdAt;
doc.updatedAt = stored.updatedAt;
doc.createdBy = stored.createdBy;
doc.metadata = stored.metadata;
await doc.save();
}
}
@@ -277,6 +314,32 @@ export class RouteConfigManager {
}
}
// =========================================================================
// Re-resolve routes after profile/target changes
// =========================================================================
/**
* Re-resolve specific routes by ID (after a profile or target is updated).
* Persists each route and calls applyRoutes() once at the end.
*/
public async reResolveRoutes(routeIds: string[]): Promise<void> {
if (!this.referenceResolver || routeIds.length === 0) return;
for (const routeId of routeIds) {
const stored = this.storedRoutes.get(routeId);
if (!stored?.metadata) continue;
const resolved = this.referenceResolver.resolveRoute(stored.route, stored.metadata);
stored.route = resolved.route;
stored.metadata = resolved.metadata;
stored.updatedAt = Date.now();
await this.persistRoute(stored);
}
await this.applyRoutes();
logger.log('info', `Re-resolved ${routeIds.length} route(s) after profile/target change`);
}
// =========================================================================
// Private: apply merged routes to SmartProxy
// =========================================================================