import * as plugins from './coreflow.plugins.js'; import { Coreflow } from './coreflow.classes.coreflow.js'; import { logger } from './coreflow.logging.js'; export interface IExternalGatewayConfig { url: string; apiToken: string; workHosterType: 'cloudly'; workHosterId: string; targetHost?: string; targetPort?: number; } interface IWorkAppRouteSyncResult { success: boolean; action?: 'created' | 'updated' | 'deleted' | 'unchanged'; routeId?: string; message?: string; } interface IDcRouterCertificateExport { success: boolean; cert?: { id: string; domainName: string; created: number; validUntil: number; privateKey: string; publicKey: string; csr: string; }; message?: string; } export class ExternalGatewayConnector { constructor(public coreflowRef: Coreflow) {} public isConfigured(configArg?: IExternalGatewayConfig): configArg is IExternalGatewayConfig { return Boolean( configArg?.url && configArg.apiToken && configArg.workHosterId && configArg.targetHost && configArg.targetPort, ); } public async syncWorkAppRoute(optionsArg: { config: IExternalGatewayConfig | undefined; service: plugins.servezoneInterfaces.data.IService; hostname: string; }): Promise { const config = optionsArg.config; if (!this.isConfigured(config)) return; const result = await this.fireDcRouterRequest( config, 'syncWorkAppRoute', { ownership: { workHosterType: 'cloudly', workHosterId: config.workHosterId, workAppId: optionsArg.service.id || optionsArg.service.data.name, hostname: optionsArg.hostname, }, route: { name: this.routeName(optionsArg.hostname), match: { ports: [443], domains: [optionsArg.hostname], }, action: { type: 'forward', targets: [{ host: config.targetHost!, port: config.targetPort! }], tls: { mode: 'terminate', certificate: 'auto', }, websocket: { enabled: true, }, }, }, enabled: true, }, ); if (!result.success) { throw new Error(result.message || `dcrouter route sync failed for ${optionsArg.hostname}`); } logger.log('success', `external gateway route ${result.action || 'synced'} for ${optionsArg.hostname}`); } public async exportCertificateForDomain( configArg: IExternalGatewayConfig | undefined, hostnameArg: string, ): Promise { if (!configArg?.url || !configArg.apiToken) return undefined; const result = await this.fireDcRouterRequest( configArg, 'exportCertificate', { domain: hostnameArg }, ); if (!result.success || !result.cert) return undefined; return { id: result.cert.id, domainName: result.cert.domainName, created: result.cert.created, validUntil: result.cert.validUntil, privateKey: result.cert.privateKey, publicKey: result.cert.publicKey, csr: result.cert.csr, }; } private routeName(hostnameArg: string): string { return `cloudly-${hostnameArg.replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-|-$/g, '')}`; } private async fireDcRouterRequest( configArg: IExternalGatewayConfig, methodArg: string, requestDataArg: Record, ): Promise { const typedRequest = new plugins.typedrequest.TypedRequest( `${configArg.url.replace(/\/+$/, '')}/typedrequest`, methodArg, ); return await typedRequest.fire({ ...requestDataArg, apiToken: configArg.apiToken, }) as TResponse; } }