import * as plugins from '../../plugins.ts'; import { logger } from '../../logging.ts'; import type { OpsServer } from '../classes.opsserver.ts'; import * as interfaces from '../../../ts_interfaces/index.ts'; import { requireValidIdentity } from '../helpers/guards.ts'; export class PlatformHandler { public typedrouter = new plugins.typedrequest.TypedRouter(); constructor(private opsServerRef: OpsServer) { this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter); this.registerHandlers(); } private registerHandlers(): void { // Get all platform services this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getPlatformServices', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const platformServices = this.opsServerRef.oneboxRef.platformServices.getAllPlatformServices(); const providers = this.opsServerRef.oneboxRef.platformServices.getAllProviders(); const result = providers.map((provider: any) => { const service = platformServices.find((s: any) => s.type === provider.type); const isCore = 'isCore' in provider && (provider as any).isCore === true; let status: string = service?.status || 'not-deployed'; if (provider.type === 'caddy') { const proxyStatus = this.opsServerRef.oneboxRef.reverseProxy.getStatus() as any; status = (proxyStatus.running ?? proxyStatus.http?.running) ? 'running' : 'stopped'; } return { type: provider.type, displayName: provider.displayName, resourceTypes: provider.resourceTypes, status: status as interfaces.data.TPlatformServiceStatus, containerId: service?.containerId, isCore, createdAt: service?.createdAt, updatedAt: service?.updatedAt, }; }); return { platformServices: result as interfaces.data.IPlatformService[] }; }, ), ); // Get specific platform service this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getPlatformService', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const provider = this.opsServerRef.oneboxRef.platformServices.getProvider(dataArg.serviceType); if (!provider) { throw new plugins.typedrequest.TypedResponseError(`Unknown platform service type: ${dataArg.serviceType}`); } const service = this.opsServerRef.oneboxRef.database.getPlatformServiceByType(dataArg.serviceType); const isCore = 'isCore' in provider && (provider as any).isCore === true; let rawStatus: string = service?.status || 'not-deployed'; if (dataArg.serviceType === 'caddy') { const proxyStatus = this.opsServerRef.oneboxRef.reverseProxy.getStatus() as any; rawStatus = (proxyStatus.running ?? proxyStatus.http?.running) ? 'running' : 'stopped'; } return { platformService: { type: provider.type, displayName: provider.displayName, resourceTypes: provider.resourceTypes, status: rawStatus as interfaces.data.TPlatformServiceStatus, containerId: service?.containerId, isCore, createdAt: service?.createdAt, updatedAt: service?.updatedAt, } as interfaces.data.IPlatformService, }; }, ), ); // Start platform service this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'startPlatformService', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const provider = this.opsServerRef.oneboxRef.platformServices.getProvider(dataArg.serviceType); if (!provider) { throw new plugins.typedrequest.TypedResponseError(`Unknown platform service type: ${dataArg.serviceType}`); } logger.info(`Starting platform service: ${dataArg.serviceType}`); const service = await this.opsServerRef.oneboxRef.platformServices.ensureRunning(dataArg.serviceType); return { platformService: { type: service.type, displayName: provider.displayName, resourceTypes: provider.resourceTypes, status: service.status, containerId: service.containerId, }, }; }, ), ); // Stop platform service this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'stopPlatformService', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const provider = this.opsServerRef.oneboxRef.platformServices.getProvider(dataArg.serviceType); if (!provider) { throw new plugins.typedrequest.TypedResponseError(`Unknown platform service type: ${dataArg.serviceType}`); } const isCore = 'isCore' in provider && (provider as any).isCore === true; if (isCore) { throw new plugins.typedrequest.TypedResponseError( `${provider.displayName} is a core service and cannot be stopped`, ); } logger.info(`Stopping platform service: ${dataArg.serviceType}`); await this.opsServerRef.oneboxRef.platformServices.stopPlatformService(dataArg.serviceType); return { platformService: { type: dataArg.serviceType, displayName: provider.displayName, resourceTypes: provider.resourceTypes, status: 'stopped' as const, }, }; }, ), ); // Get platform service stats this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getPlatformServiceStats', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const service = this.opsServerRef.oneboxRef.database.getPlatformServiceByType(dataArg.serviceType); if (!service || !service.containerId) { throw new plugins.typedrequest.TypedResponseError('Platform service has no container'); } const stats = await this.opsServerRef.oneboxRef.docker.getContainerStats(service.containerId); if (!stats) { throw new plugins.typedrequest.TypedResponseError('Could not retrieve container stats'); } return { stats }; }, ), ); // Get platform service logs this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getPlatformServiceLogs', async (dataArg) => { await requireValidIdentity(this.opsServerRef.adminHandler, dataArg); const service = this.opsServerRef.oneboxRef.database.getPlatformServiceByType(dataArg.serviceType); if (!service || !service.containerId) { throw new plugins.typedrequest.TypedResponseError('Platform service has no container'); } const tail = dataArg.tail || 100; const rawLogs = await this.opsServerRef.oneboxRef.docker.getContainerLogs(service.containerId, tail); // Parse raw log output into structured entries const logLines = (rawLogs.stdout + rawLogs.stderr) .split('\n') .filter((line: string) => line.trim()); const logs = logLines.map((line: string, index: number) => { const isError = line.toLowerCase().includes('error') || line.toLowerCase().includes('fatal'); const isWarn = line.toLowerCase().includes('warn'); return { id: index, serviceId: 0, timestamp: Date.now(), message: line, level: (isError ? 'error' : isWarn ? 'warn' : 'info') as 'info' | 'warn' | 'error' | 'debug', source: 'stdout' as const, }; }); return { logs }; }, ), ); } }