import * as plugins from '../../plugins.js'; import type { OpsServer } from '../classes.opsserver.js'; import * as interfaces from '../../../ts_interfaces/index.js'; /** * CRUD + connection-test handlers for DnsProviderDoc. * * Auth: same dual-mode pattern as TargetProfileHandler — admin JWT or * API token with the appropriate `dns-providers:read|write` scope. */ export class DnsProviderHandler { public typedrouter = new plugins.typedrequest.TypedRouter(); constructor(private opsServerRef: OpsServer) { this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter); this.registerHandlers(); } private async requireAuth( request: { identity?: interfaces.data.IIdentity; apiToken?: string }, requiredScope?: interfaces.data.TApiTokenScope, ): Promise { 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 */ } } 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 all providers — prepends the built-in DcRouter pseudo-provider // so operators see a uniform "who serves this?" list that includes the // authoritative dcrouter alongside external accounts. this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getDnsProviders', async (dataArg) => { await this.requireAuth(dataArg, 'dns-providers:read'); const dnsManager = this.opsServerRef.dcRouterRef.dnsManager; const synthetic: interfaces.data.IDnsProviderPublic = { id: interfaces.data.DCROUTER_BUILTIN_PROVIDER_ID, name: 'DcRouter', type: 'dcrouter', status: 'ok', createdAt: 0, updatedAt: 0, createdBy: 'system', hasCredentials: false, builtIn: true, }; const real = dnsManager ? await dnsManager.listProviders() : []; return { providers: [synthetic, ...real] }; }, ), ); // Get single provider this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getDnsProvider', async (dataArg) => { await this.requireAuth(dataArg, 'dns-providers:read'); const dnsManager = this.opsServerRef.dcRouterRef.dnsManager; if (!dnsManager) return { provider: null }; return { provider: await dnsManager.getProvider(dataArg.id) }; }, ), ); // Create provider this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'createDnsProvider', async (dataArg) => { const userId = await this.requireAuth(dataArg, 'dns-providers:write'); if (dataArg.type === 'dcrouter') { return { success: false, message: 'cannot create built-in provider', }; } const dnsManager = this.opsServerRef.dcRouterRef.dnsManager; if (!dnsManager) { return { success: false, message: 'DnsManager not initialized (DB disabled?)' }; } const id = await dnsManager.createProvider({ name: dataArg.name, type: dataArg.type, credentials: dataArg.credentials, createdBy: userId, }); return { success: true, id }; }, ), ); // Update provider this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'updateDnsProvider', async (dataArg) => { await this.requireAuth(dataArg, 'dns-providers:write'); if (dataArg.id === interfaces.data.DCROUTER_BUILTIN_PROVIDER_ID) { return { success: false, message: 'cannot edit built-in provider' }; } const dnsManager = this.opsServerRef.dcRouterRef.dnsManager; if (!dnsManager) return { success: false, message: 'DnsManager not initialized' }; const ok = await dnsManager.updateProvider(dataArg.id, { name: dataArg.name, credentials: dataArg.credentials, }); return ok ? { success: true } : { success: false, message: 'Provider not found' }; }, ), ); // Delete provider this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'deleteDnsProvider', async (dataArg) => { await this.requireAuth(dataArg, 'dns-providers:write'); if (dataArg.id === interfaces.data.DCROUTER_BUILTIN_PROVIDER_ID) { return { success: false, message: 'cannot delete built-in provider' }; } const dnsManager = this.opsServerRef.dcRouterRef.dnsManager; if (!dnsManager) return { success: false, message: 'DnsManager not initialized' }; return await dnsManager.deleteProvider(dataArg.id, dataArg.force ?? false); }, ), ); // Test provider connection this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'testDnsProvider', async (dataArg) => { await this.requireAuth(dataArg, 'dns-providers:read'); if (dataArg.id === interfaces.data.DCROUTER_BUILTIN_PROVIDER_ID) { return { ok: false, error: 'built-in provider has no external connection to test', testedAt: Date.now(), }; } const dnsManager = this.opsServerRef.dcRouterRef.dnsManager; if (!dnsManager) { return { ok: false, error: 'DnsManager not initialized', testedAt: Date.now() }; } return await dnsManager.testProvider(dataArg.id); }, ), ); // List domains visible to a provider's account (without importing them) this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'listProviderDomains', async (dataArg) => { await this.requireAuth(dataArg, 'dns-providers:read'); if (dataArg.providerId === interfaces.data.DCROUTER_BUILTIN_PROVIDER_ID) { return { success: false, message: 'built-in provider has no external domain listing — use "Add DcRouter Domain" instead', }; } const dnsManager = this.opsServerRef.dcRouterRef.dnsManager; if (!dnsManager) return { success: false, message: 'DnsManager not initialized' }; try { const domains = await dnsManager.listProviderDomains(dataArg.providerId); return { success: true, domains }; } catch (err: unknown) { return { success: false, message: (err as Error).message }; } }, ), ); } }