import { Controller, Get, Inject, UseGuards } from '@nestjs/common'; import { sql, type Db } from '@mosaicstack/db'; import { createQueue } from '@mosaicstack/queue'; import { DB } from '../database/database.module.js'; import { AgentService } from '../agent/agent.service.js'; import { ProviderService } from '../agent/provider.service.js'; import { AdminGuard } from './admin.guard.js'; import type { HealthStatusDto, ServiceStatusDto } from './admin.dto.js'; @Controller('api/admin/health') @UseGuards(AdminGuard) export class AdminHealthController { constructor( @Inject(DB) private readonly db: Db, @Inject(AgentService) private readonly agentService: AgentService, @Inject(ProviderService) private readonly providerService: ProviderService, ) {} @Get() async check(): Promise { const [database, cache] = await Promise.all([this.checkDatabase(), this.checkCache()]); const sessions = this.agentService.listSessions(); const providers = this.providerService.listProviders(); const allOk = database.status === 'ok' && cache.status === 'ok'; return { status: allOk ? 'ok' : 'degraded', database, cache, agentPool: { activeSessions: sessions.length }, providers: providers.map((p) => ({ id: p.id, name: p.name, available: p.available, modelCount: p.models.length, })), checkedAt: new Date().toISOString(), }; } private async checkDatabase(): Promise { const start = Date.now(); try { await this.db.execute(sql`SELECT 1`); return { status: 'ok', latencyMs: Date.now() - start }; } catch (err) { return { status: 'error', latencyMs: Date.now() - start, error: err instanceof Error ? err.message : String(err), }; } } private async checkCache(): Promise { const start = Date.now(); const handle = createQueue(); try { await handle.redis.ping(); return { status: 'ok', latencyMs: Date.now() - start }; } catch (err) { return { status: 'error', latencyMs: Date.now() - start, error: err instanceof Error ? err.message : String(err), }; } finally { await handle.close().catch(() => {}); } } }