feat(routes,email): persist system DNS routes with runtime hydration and add reusable email ops DNS helpers
This commit is contained in:
@@ -40,7 +40,7 @@ const clearTestState = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
tap.test('RouteConfigManager applies runtime DoH routes without persisting them', async () => {
|
||||
tap.test('RouteConfigManager persists DoH system routes and hydrates runtime socket handlers', async () => {
|
||||
await testDbPromise;
|
||||
await clearTestState();
|
||||
|
||||
@@ -64,15 +64,24 @@ tap.test('RouteConfigManager applies runtime DoH routes without persisting them'
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
() => (dcRouter as any).generateDnsRoutes(),
|
||||
undefined,
|
||||
(storedRoute: any) => (dcRouter as any).hydrateStoredRouteForRuntime(storedRoute),
|
||||
);
|
||||
|
||||
await routeManager.initialize([], [], []);
|
||||
await routeManager.applyRoutes();
|
||||
await routeManager.initialize([], [], (dcRouter as any).generateDnsRoutes({ includeSocketHandler: false }));
|
||||
|
||||
const persistedRoutes = await RouteDoc.findAll();
|
||||
expect(persistedRoutes.length).toEqual(0);
|
||||
expect(appliedRoutes.length).toEqual(2);
|
||||
expect(persistedRoutes.length).toEqual(2);
|
||||
expect(persistedRoutes.every((route) => route.origin === 'dns')).toEqual(true);
|
||||
expect((await RouteDoc.findByName('dns-over-https-dns-query'))?.systemKey).toEqual('dns:dns-over-https-dns-query');
|
||||
expect((await RouteDoc.findByName('dns-over-https-resolve'))?.systemKey).toEqual('dns:dns-over-https-resolve');
|
||||
|
||||
const mergedRoutes = routeManager.getMergedRoutes().routes;
|
||||
expect(mergedRoutes.length).toEqual(2);
|
||||
expect(mergedRoutes.every((route) => route.origin === 'dns')).toEqual(true);
|
||||
expect(mergedRoutes.every((route) => route.systemKey?.startsWith('dns:'))).toEqual(true);
|
||||
|
||||
expect(appliedRoutes.length).toEqual(1);
|
||||
|
||||
for (const routeSet of appliedRoutes) {
|
||||
const dnsQueryRoute = routeSet.find((route) => route.name === 'dns-over-https-dns-query');
|
||||
@@ -85,10 +94,17 @@ tap.test('RouteConfigManager applies runtime DoH routes without persisting them'
|
||||
}
|
||||
});
|
||||
|
||||
tap.test('RouteConfigManager removes stale persisted DoH socket-handler routes on startup', async () => {
|
||||
tap.test('RouteConfigManager backfills existing DoH system routes by name without duplicating them', async () => {
|
||||
await testDbPromise;
|
||||
await clearTestState();
|
||||
|
||||
const dcRouter = new DcRouter({
|
||||
dnsNsDomains: ['ns1.example.com', 'ns2.example.com'],
|
||||
dnsScopes: ['example.com'],
|
||||
smartProxyConfig: { routes: [] },
|
||||
dbConfig: { enabled: false },
|
||||
});
|
||||
|
||||
const staleDnsQueryRoute = new RouteDoc();
|
||||
staleDnsQueryRoute.id = 'stale-doh-query';
|
||||
staleDnsQueryRoute.route = {
|
||||
@@ -109,47 +125,6 @@ tap.test('RouteConfigManager removes stale persisted DoH socket-handler routes o
|
||||
staleDnsQueryRoute.origin = 'dns';
|
||||
await staleDnsQueryRoute.save();
|
||||
|
||||
const staleResolveRoute = new RouteDoc();
|
||||
staleResolveRoute.id = 'stale-doh-resolve';
|
||||
staleResolveRoute.route = {
|
||||
name: 'dns-over-https-resolve',
|
||||
match: {
|
||||
ports: [443],
|
||||
domains: ['ns1.example.com'],
|
||||
path: '/resolve',
|
||||
},
|
||||
action: {
|
||||
type: 'socket-handler' as any,
|
||||
} as any,
|
||||
};
|
||||
staleResolveRoute.enabled = true;
|
||||
staleResolveRoute.createdAt = Date.now();
|
||||
staleResolveRoute.updatedAt = Date.now();
|
||||
staleResolveRoute.createdBy = 'test';
|
||||
staleResolveRoute.origin = 'dns';
|
||||
await staleResolveRoute.save();
|
||||
|
||||
const validRoute = new RouteDoc();
|
||||
validRoute.id = 'valid-forward-route';
|
||||
validRoute.route = {
|
||||
name: 'valid-forward-route',
|
||||
match: {
|
||||
ports: [443],
|
||||
domains: ['app.example.com'],
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{ host: '127.0.0.1', port: 8443 }],
|
||||
tls: { mode: 'terminate' as const },
|
||||
},
|
||||
} as any;
|
||||
validRoute.enabled = true;
|
||||
validRoute.createdAt = Date.now();
|
||||
validRoute.updatedAt = Date.now();
|
||||
validRoute.createdBy = 'test';
|
||||
validRoute.origin = 'api';
|
||||
await validRoute.save();
|
||||
|
||||
const appliedRoutes: any[][] = [];
|
||||
const smartProxy = {
|
||||
updateRoutes: async (routes: any[]) => {
|
||||
@@ -157,19 +132,76 @@ tap.test('RouteConfigManager removes stale persisted DoH socket-handler routes o
|
||||
},
|
||||
};
|
||||
|
||||
const routeManager = new RouteConfigManager(() => smartProxy as any);
|
||||
await routeManager.initialize([], [], []);
|
||||
|
||||
expect((await RouteDoc.findByName('dns-over-https-dns-query'))).toEqual(null);
|
||||
expect((await RouteDoc.findByName('dns-over-https-resolve'))).toEqual(null);
|
||||
const routeManager = new RouteConfigManager(
|
||||
() => smartProxy as any,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
(storedRoute: any) => (dcRouter as any).hydrateStoredRouteForRuntime(storedRoute),
|
||||
);
|
||||
await routeManager.initialize([], [], (dcRouter as any).generateDnsRoutes({ includeSocketHandler: false }));
|
||||
|
||||
const remainingRoutes = await RouteDoc.findAll();
|
||||
expect(remainingRoutes.length).toEqual(1);
|
||||
expect(remainingRoutes[0].route.name).toEqual('valid-forward-route');
|
||||
expect(remainingRoutes.length).toEqual(2);
|
||||
expect(remainingRoutes.filter((route) => route.route.name === 'dns-over-https-dns-query').length).toEqual(1);
|
||||
expect(remainingRoutes.filter((route) => route.route.name === 'dns-over-https-resolve').length).toEqual(1);
|
||||
|
||||
const queryRoute = await RouteDoc.findByName('dns-over-https-dns-query');
|
||||
expect(queryRoute?.id).toEqual('stale-doh-query');
|
||||
expect(queryRoute?.systemKey).toEqual('dns:dns-over-https-dns-query');
|
||||
|
||||
const resolveRoute = await RouteDoc.findByName('dns-over-https-resolve');
|
||||
expect(resolveRoute?.systemKey).toEqual('dns:dns-over-https-resolve');
|
||||
|
||||
expect(appliedRoutes.length).toEqual(1);
|
||||
expect(appliedRoutes[0].length).toEqual(1);
|
||||
expect(appliedRoutes[0][0].name).toEqual('valid-forward-route');
|
||||
expect(appliedRoutes[0].length).toEqual(2);
|
||||
expect(appliedRoutes[0].every((route) => typeof route.action.socketHandler === 'function')).toEqual(true);
|
||||
});
|
||||
|
||||
tap.test('RouteConfigManager only allows toggling system routes', async () => {
|
||||
await testDbPromise;
|
||||
await clearTestState();
|
||||
|
||||
const smartProxy = {
|
||||
updateRoutes: async (_routes: any[]) => {
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
const routeManager = new RouteConfigManager(() => smartProxy as any);
|
||||
await routeManager.initialize([
|
||||
{
|
||||
name: 'system-config-route',
|
||||
match: {
|
||||
ports: [443],
|
||||
domains: ['app.example.com'],
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{ host: '127.0.0.1', port: 8443 }],
|
||||
tls: { mode: 'terminate' as const },
|
||||
},
|
||||
} as any,
|
||||
], [], []);
|
||||
|
||||
const systemRoute = routeManager.getMergedRoutes().routes.find((route) => route.route.name === 'system-config-route');
|
||||
expect(systemRoute).toBeDefined();
|
||||
|
||||
const updateResult = await routeManager.updateRoute(systemRoute!.id, {
|
||||
route: { name: 'renamed-system-route' } as any,
|
||||
});
|
||||
expect(updateResult.success).toEqual(false);
|
||||
expect(updateResult.message).toEqual('System routes are managed by the system and can only be toggled');
|
||||
|
||||
const deleteResult = await routeManager.deleteRoute(systemRoute!.id);
|
||||
expect(deleteResult.success).toEqual(false);
|
||||
expect(deleteResult.message).toEqual('System routes are managed by the system and cannot be deleted');
|
||||
|
||||
const toggleResult = await routeManager.toggleRoute(systemRoute!.id, false);
|
||||
expect(toggleResult.success).toEqual(true);
|
||||
expect((await RouteDoc.findById(systemRoute!.id))?.enabled).toEqual(false);
|
||||
});
|
||||
|
||||
tap.test('DnsManager warning keeps dnsNsDomains in scope', async () => {
|
||||
|
||||
Reference in New Issue
Block a user