openvidu/openvidu-dashboard/dashboard-ce/backend/src/api/probes.ts

70 lines
2.3 KiB
TypeScript
Raw Normal View History

import type { Db } from 'mongodb';
import * as z from 'zod';
import { type ReadyChecks, WriterSchemaStatus } from '@openvidu-dashboard/typings';
import { ApiError } from '../http/api-error.js';
import { readSchemaStatus } from '../schema/schema-status.js';
import type { RouteRegistry } from './route-registry.js';
export const READY_PING_TIMEOUT_MS = 2_000;
const healthSchema = z.object({ status: z.literal('ok'), version: z.string() }).meta({ id: 'HealthResponse' });
const readySchema = z
.object({
status: z.literal('ready'),
checks: z.object({ mongo: z.enum(['ok', 'error']), schema: z.enum(WriterSchemaStatus).optional() })
})
.meta({ id: 'ReadyResponse' });
/** MongoDB answers a ping within 2 s and the writer schema is not older than this dashboard reads. */
export async function readiness(db: Db): Promise<{ ready: boolean; checks: ReadyChecks }> {
try {
await db.command({ ping: 1 }, { timeoutMS: READY_PING_TIMEOUT_MS });
} catch {
return { ready: false, checks: { mongo: 'error' } };
}
const { status } = await readSchemaStatus(db);
return { ready: status !== WriterSchemaStatus.OLDER, checks: { mongo: 'ok', schema: status } };
}
/** `GET /health` (liveness, no MongoDB) and `GET /ready` (readiness). */
export function registerProbes(registry: RouteRegistry, version: string, db: Db): void {
registry.tag('Probes', 'Liveness and readiness of the dashboard, for orchestrators and load balancers');
registry.register({
method: 'get',
path: '/health',
operationId: 'getHealth',
edition: 'ce',
summary: 'Liveness: the process answers; MongoDB is not checked',
tags: ['Probes'],
security: 'public',
responses: { 200: { description: 'Alive', schema: healthSchema } },
handler: () => ({ status: 'ok', version })
});
registry.register({
method: 'get',
path: '/ready',
operationId: 'getReadiness',
edition: 'ce',
summary: 'Readiness: MongoDB answers and the writer schema is supported',
tags: ['Probes'],
security: 'public',
responses: {
200: { description: 'Ready', schema: readySchema },
503: { description: 'Not ready; details.checks says why' }
},
handler: async () => {
const { ready, checks } = await readiness(db);
if (!ready) {
throw new ApiError('NOT_READY', 'The dashboard is not ready', { checks });
}
return { status: 'ready', checks };
}
});
}