feat(api): add Prometheus metrics and OpenTelemetry instrumentation for HTTP, auth, classifications, and diagnoses

This commit is contained in:
MythEclipse
2026-06-07 18:12:32 +07:00
parent 3b7ccad619
commit ede1c480be
9 changed files with 128 additions and 1 deletions
+6
View File
@@ -16,6 +16,7 @@ import {
verifyPassword,
} from '../lib/auth';
import { env } from '../config/env';
import { authCounter } from '../lib/telemetry';
function normalizeEmail(email: unknown) {
return typeof email === 'string' ? email.trim().toLowerCase() : '';
@@ -63,6 +64,8 @@ export const authRoutes = new Elysia({ prefix: '/api/v1/auth' })
const token = await createSession(user.id);
set.headers['Set-Cookie'] = createSessionCookie(token);
authCounter.labels('register', 'true').inc();
return {
user: {
id: user.id,
@@ -90,12 +93,15 @@ export const authRoutes = new Elysia({ prefix: '/api/v1/auth' })
const user = rows[0];
if (!user?.passwordHash || !(await verifyPassword(req!.password!, user.passwordHash))) {
authCounter.labels('login', 'false').inc();
return badRequest('Invalid email or password');
}
const token = await createSession(user.id);
set.headers['Set-Cookie'] = createSessionCookie(token);
authCounter.labels('login', 'true').inc();
return {
user: {
id: user.id,
+3
View File
@@ -16,6 +16,7 @@ import { desc, eq } from 'drizzle-orm';
import { classifyImage } from '../lib/image-model';
import { uploadImageToStorage } from '../lib/uploader-client';
import { toDisease } from '../lib/disease-mappers';
import { classificationCounter } from '../lib/telemetry';
function toImageClassificationRecord(row: {
id: string;
@@ -141,6 +142,8 @@ export const classificationRoutes = new Elysia({ prefix: '/api/v1' })
);
}
classificationCounter.labels(classificationResult.predictedDiseaseSlug).inc();
const db = createDbClient();
let diseaseRow;
try {
+7
View File
@@ -9,6 +9,7 @@ import { getCurrentUser } from '../lib/auth';
import { classifyImage } from '../lib/image-model';
import { uploadImageToStorage } from '../lib/uploader-client';
import { env } from '../config/env';
import { diagnosisCounter } from '../lib/telemetry';
interface ReviewRow {
reviewId: string | null;
@@ -222,6 +223,9 @@ export const diagnosisRoutes = new Elysia({ prefix: '/api/v1' })
const record = await loadDiagnosisRecord(inserted[0].id, user.id, false);
if (!record) return serviceUnavailable('Database unavailable');
diagnosisCounter.labels(record.predictedDiseaseSlug ?? 'unknown').inc();
return record;
} catch (error) {
const inserted = await db
@@ -240,6 +244,9 @@ export const diagnosisRoutes = new Elysia({ prefix: '/api/v1' })
const record = await loadDiagnosisRecord(inserted[0].id, user.id, false);
if (!record) return serviceUnavailable('Database unavailable');
diagnosisCounter.labels('failed').inc();
return record;
}
})
+10
View File
@@ -0,0 +1,10 @@
import { Elysia } from 'elysia';
import { getMetrics, getMetricsContentType } from '../lib/telemetry';
export const metricsRoutes = new Elysia()
.get('/metrics', async () => {
const body = await getMetrics();
return new Response(body, {
headers: { 'Content-Type': getMetricsContentType() },
});
});