Files
CASAN/packages/casan-control-panel/backend/src/telemetry/telemetry.controller.ts
T

103 lines
2.6 KiB
TypeScript

// Read-only Ops Console API. Every handler returns the standard ok() envelope. No writes,
// no auth (loopback-bound, "Đọc ≠ Ghi"); management/RBAC is Plan-13 Track 2/3 (future).
import { Controller, Get, Inject, Param, Query, Req, Res } from '@nestjs/common';
import type { Request, Response } from 'express';
import { ok } from '../common/api-response.js';
import { TelemetryService } from './telemetry.service.js';
@Controller('api/v1')
export class TelemetryController {
// Explicit @Inject token so DI works under BOTH tsc (emits decorator metadata) and the
// tsx/esbuild dev runner (which does not emit design:paramtypes).
constructor(@Inject(TelemetryService) private readonly svc: TelemetryService) {}
@Get('overview')
overview() {
return ok(this.svc.overview());
}
@Get('runs')
runs(@Query('limit') limit?: string) {
const n = Math.min(Math.max(Number(limit) || 50, 1), 500);
const r = this.svc.runs(n);
return ok(r, { total: r.count });
}
@Get('runs/:traceId')
run(@Param('traceId') traceId: string) {
return ok(this.svc.run(traceId));
}
@Get('runs/:traceId/graph')
traceGraph(@Param('traceId') traceId: string) {
return ok(this.svc.traceGraph(traceId));
}
@Get('runs/:traceId/events')
traceEvents(@Param('traceId') traceId: string, @Req() req: Request, @Res() res: Response) {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache, no-transform');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
let previous = '';
const publish = () => {
const graph = this.svc.traceGraph(traceId);
const serialized = JSON.stringify(graph);
if (serialized !== previous) {
res.write(`event: trace\ndata: ${serialized}\n\n`);
previous = serialized;
} else {
res.write(': heartbeat\n\n');
}
};
const timer = setInterval(publish, 750);
const close = () => {
clearInterval(timer);
if (!res.writableEnded) res.end();
};
req.on('close', close);
publish();
}
@Get('governance')
governance() {
return ok(this.svc.governance());
}
@Get('security')
security() {
return ok(this.svc.security());
}
@Get('incidents')
incidents() {
return ok(this.svc.incidents());
}
@Get('tools')
tools() {
return ok(this.svc.tools());
}
@Get('traceability')
traceability() {
return ok(this.svc.traceability());
}
@Get('drift')
drift() {
return ok(this.svc.drift());
}
@Get('cost')
cost() {
return ok(this.svc.cost());
}
@Get('command')
command() {
return ok(this.svc.commandCenter());
}
}