import * as plugins from '../../plugins.js'; import * as paths from '../../paths.js'; import type { OpsServer } from '../classes.opsserver.js'; import * as interfaces from '../../../ts_interfaces/index.js'; export class ConfigHandler { public typedrouter = new plugins.typedrequest.TypedRouter(); constructor(private opsServerRef: OpsServer) { // Add this handler's router to the parent this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter); this.registerHandlers(); } private registerHandlers(): void { // Get Configuration Handler (read-only) this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getConfiguration', async (dataArg, toolsArg) => { const config = await this.getConfiguration(); return { config, section: dataArg.section, }; } ) ); } private async getConfiguration(): Promise { const dcRouter = this.opsServerRef.dcRouterRef; const opts = dcRouter.options; const resolvedPaths = dcRouter.resolvedPaths; // --- System --- const storageBackend: 'filesystem' | 'custom' | 'memory' = opts.storage?.readFunction ? 'custom' : opts.storage?.fsPath ? 'filesystem' : 'memory'; const system: interfaces.requests.IConfigData['system'] = { baseDir: resolvedPaths.dcrouterHomeDir, dataDir: resolvedPaths.dataDir, publicIp: opts.publicIp || null, proxyIps: opts.proxyIps || [], uptime: Math.floor(process.uptime()), storageBackend, storagePath: opts.storage?.fsPath || null, }; // --- SmartProxy --- let acmeInfo: interfaces.requests.IConfigData['smartProxy']['acme'] = null; if (opts.smartProxyConfig?.acme) { const acme = opts.smartProxyConfig.acme; acmeInfo = { enabled: acme.enabled !== false, accountEmail: acme.accountEmail || '', useProduction: acme.useProduction !== false, autoRenew: acme.autoRenew !== false, renewThresholdDays: acme.renewThresholdDays || 30, }; } let routeCount = 0; if (dcRouter.routeConfigManager) { try { const merged = await dcRouter.routeConfigManager.getMergedRoutes(); routeCount = merged.routes.length; } catch { routeCount = opts.smartProxyConfig?.routes?.length || 0; } } else if (opts.smartProxyConfig?.routes) { routeCount = opts.smartProxyConfig.routes.length; } const smartProxy: interfaces.requests.IConfigData['smartProxy'] = { enabled: !!dcRouter.smartProxy, routeCount, acme: acmeInfo, }; // --- Email --- let emailDomains: string[] = []; if (dcRouter.emailServer && (dcRouter.emailServer as any).domainRegistry) { emailDomains = (dcRouter.emailServer as any).domainRegistry.getAllDomains(); } else if (opts.emailConfig?.domains) { emailDomains = opts.emailConfig.domains.map((d: any) => typeof d === 'string' ? d : d.domain ); } let portMapping: Record | null = null; if (opts.emailPortConfig?.portMapping) { portMapping = {}; for (const [ext, int] of Object.entries(opts.emailPortConfig.portMapping)) { portMapping[String(ext)] = int as number; } } const email: interfaces.requests.IConfigData['email'] = { enabled: !!dcRouter.emailServer, ports: opts.emailConfig?.ports || [], portMapping, hostname: opts.emailConfig?.hostname || null, domains: emailDomains, emailRouteCount: opts.emailConfig?.routes?.length || 0, receivedEmailsPath: opts.emailPortConfig?.receivedEmailsPath || null, }; // --- DNS --- const dnsRecords = (opts.dnsRecords || []).map(r => ({ name: r.name, type: r.type, value: r.value, ttl: r.ttl, })); const dns: interfaces.requests.IConfigData['dns'] = { enabled: !!dcRouter.dnsServer, port: 53, nsDomains: opts.dnsNsDomains || [], scopes: opts.dnsScopes || [], recordCount: dnsRecords.length, records: dnsRecords, dnsChallenge: !!opts.dnsChallenge?.cloudflareApiKey, }; // --- TLS --- let tlsSource: 'acme' | 'static' | 'none' = 'none'; if (opts.tls?.certPath && opts.tls?.keyPath) { tlsSource = 'static'; } else if (opts.smartProxyConfig?.acme?.enabled !== false && opts.smartProxyConfig?.acme) { tlsSource = 'acme'; } const tls: interfaces.requests.IConfigData['tls'] = { contactEmail: opts.tls?.contactEmail || opts.smartProxyConfig?.acme?.accountEmail || null, domain: opts.tls?.domain || null, source: tlsSource, certPath: opts.tls?.certPath || null, keyPath: opts.tls?.keyPath || null, }; // --- Cache --- const cacheConfig = opts.cacheConfig; const cache: interfaces.requests.IConfigData['cache'] = { enabled: cacheConfig?.enabled !== false, storagePath: cacheConfig?.storagePath || resolvedPaths.defaultTsmDbPath, dbName: cacheConfig?.dbName || 'dcrouter', defaultTTLDays: cacheConfig?.defaultTTLDays || 30, cleanupIntervalHours: cacheConfig?.cleanupIntervalHours || 1, ttlConfig: cacheConfig?.ttlConfig ? { ...cacheConfig.ttlConfig } as Record : {}, }; // --- RADIUS --- const radiusCfg = opts.radiusConfig; const radius: interfaces.requests.IConfigData['radius'] = { enabled: !!dcRouter.radiusServer, authPort: radiusCfg?.authPort || null, acctPort: radiusCfg?.acctPort || null, bindAddress: radiusCfg?.bindAddress || null, clientCount: radiusCfg?.clients?.length || 0, vlanDefaultVlan: radiusCfg?.vlanAssignment?.defaultVlan ?? null, vlanAllowUnknownMacs: radiusCfg?.vlanAssignment?.allowUnknownMacs ?? null, vlanMappingCount: radiusCfg?.vlanAssignment?.mappings?.length || 0, }; // --- Remote Ingress --- const riCfg = opts.remoteIngressConfig; const remoteIngress: interfaces.requests.IConfigData['remoteIngress'] = { enabled: !!dcRouter.remoteIngressManager, tunnelPort: riCfg?.tunnelPort || null, hubDomain: riCfg?.hubDomain || null, tlsConfigured: !!(riCfg?.tls?.certPath && riCfg?.tls?.keyPath), }; return { system, smartProxy, email, dns, tls, cache, radius, remoteIngress, }; } }