import * as plugins from '../../plugins.js'; import type { OpsServer } from '../classes.opsserver.js'; import * as interfaces from '../../../ts_interfaces/index.js'; export class RouteManagementHandler { public typedrouter = new plugins.typedrequest.TypedRouter(); constructor(private opsServerRef: OpsServer) { this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter); this.registerHandlers(); } /** * Validate auth: JWT identity OR API token with required scope. * Returns a userId string on success, throws on failure. */ private async requireAuth( request: { identity?: interfaces.data.IIdentity; apiToken?: string }, requiredScope?: interfaces.data.TApiTokenScope, ): Promise { // Try JWT identity first if (request.identity?.jwt) { try { const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({ identity: request.identity, }); if (isAdmin) return request.identity.userId; } catch { /* fall through */ } } // Try API token if (request.apiToken) { const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager; if (tokenManager) { const token = await tokenManager.validateToken(request.apiToken); if (token) { if (!requiredScope || tokenManager.hasScope(token, requiredScope)) { return token.createdBy; } throw new plugins.typedrequest.TypedResponseError('insufficient scope'); } } } throw new plugins.typedrequest.TypedResponseError('unauthorized'); } private registerHandlers(): void { // Get merged routes this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getMergedRoutes', async (dataArg) => { await this.requireAuth(dataArg, 'routes:read'); const manager = this.opsServerRef.dcRouterRef.routeConfigManager; if (!manager) { return { routes: [], warnings: [] }; } return manager.getMergedRoutes(); }, ), ); // Create route this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'createRoute', async (dataArg) => { const userId = await this.requireAuth(dataArg, 'routes:write'); const manager = this.opsServerRef.dcRouterRef.routeConfigManager; if (!manager) { return { success: false, message: 'Route management not initialized' }; } const id = await manager.createRoute(dataArg.route, userId, dataArg.enabled ?? true); return { success: true, storedRouteId: id }; }, ), ); // Update route this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'updateRoute', async (dataArg) => { await this.requireAuth(dataArg, 'routes:write'); const manager = this.opsServerRef.dcRouterRef.routeConfigManager; if (!manager) { return { success: false, message: 'Route management not initialized' }; } const ok = await manager.updateRoute(dataArg.id, { route: dataArg.route as any, enabled: dataArg.enabled, }); return { success: ok, message: ok ? undefined : 'Route not found' }; }, ), ); // Delete route this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'deleteRoute', async (dataArg) => { await this.requireAuth(dataArg, 'routes:write'); const manager = this.opsServerRef.dcRouterRef.routeConfigManager; if (!manager) { return { success: false, message: 'Route management not initialized' }; } const ok = await manager.deleteRoute(dataArg.id); return { success: ok, message: ok ? undefined : 'Route not found' }; }, ), ); // Set override on a hardcoded route this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'setRouteOverride', async (dataArg) => { const userId = await this.requireAuth(dataArg, 'routes:write'); const manager = this.opsServerRef.dcRouterRef.routeConfigManager; if (!manager) { return { success: false, message: 'Route management not initialized' }; } await manager.setOverride(dataArg.routeName, dataArg.enabled, userId); return { success: true }; }, ), ); // Remove override from a hardcoded route this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'removeRouteOverride', async (dataArg) => { await this.requireAuth(dataArg, 'routes:write'); const manager = this.opsServerRef.dcRouterRef.routeConfigManager; if (!manager) { return { success: false, message: 'Route management not initialized' }; } const ok = await manager.removeOverride(dataArg.routeName); return { success: ok, message: ok ? undefined : 'Override not found' }; }, ), ); // Toggle programmatic route this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'toggleRoute', async (dataArg) => { await this.requireAuth(dataArg, 'routes:write'); const manager = this.opsServerRef.dcRouterRef.routeConfigManager; if (!manager) { return { success: false, message: 'Route management not initialized' }; } const ok = await manager.toggleRoute(dataArg.id, dataArg.enabled); return { success: ok, message: ok ? undefined : 'Route not found' }; }, ), ); } }