Files
zeavis-edu/apps/api/src/routes/dashboard.ts
T
MythEclipseandClaude c881e6a2b2 fix(auth): pass request.headers to getCurrentUser in all protected routes
dashboard, diagnoses, expert routes now support Authorization header fallback for Android WebView

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-15 20:11:11 +07:00

75 lines
3.1 KiB
TypeScript

import { Elysia } from 'elysia';
import type { DashboardSummary } from '@zeavis/shared';
import { isDiseaseSlug } from '@zeavis/shared';
import { eq, desc, count } from 'drizzle-orm';
import { createDbClient } from '../db/client';
import { diseaseCatalog, manualClassifications, diagnoses } from '../db/schema';
import { serviceUnavailable } from '../lib/http-errors';
import { toDisease } from '../lib/disease-mappers';
import { getCurrentUser } from '../lib/auth';
import { loadDiagnosisRecord } from './diagnoses';
export const dashboardRoutes = new Elysia({ prefix: '/api/v1' })
.get('/dashboard/summary', async ({ request }) => {
try {
const db = createDbClient();
const user = await getCurrentUser(request.headers.get('cookie'), request.headers);
const diseases = await db.select().from(diseaseCatalog).orderBy(diseaseCatalog.displayOrder);
const manualRows = await db.select().from(manualClassifications).orderBy(desc(manualClassifications.createdAt)).limit(1);
const diagnosisFilters = user ? eq(diagnoses.userId, user.id) : undefined;
const diagnosisRows = diagnosisFilters
? await db.select({ id: diagnoses.id }).from(diagnoses).where(diagnosisFilters).orderBy(desc(diagnoses.createdAt)).limit(1)
: [];
const diagnosisCountRows = user
? await db.select({ value: count() }).from(diagnoses).where(eq(diagnoses.userId, user.id))
: [{ value: 0 }];
const needsReviewRows = user?.role === 'expert'
? await db.select({ value: count() }).from(diagnoses).where(eq(diagnoses.status, 'needs_review'))
: [{ value: 0 }];
const latestDiagnosis = diagnosisRows[0]
? await loadDiagnosisRecord(diagnosisRows[0].id, user?.id ?? null, user?.role === 'expert')
: null;
const riskDistribution = diseases.reduce(
(acc, disease) => {
if (disease.riskLevel === 'high') acc.high += 1;
if (disease.riskLevel === 'medium') acc.medium += 1;
if (disease.riskLevel === 'low') acc.low += 1;
return acc;
},
{ low: 0, medium: 0, high: 0 },
);
const latestManual = manualRows[0];
const latestDisease = latestManual && isDiseaseSlug(latestManual.diseaseSlug)
? diseases.find((disease) => disease.slug === latestManual.diseaseSlug)
: null;
const summary: DashboardSummary = {
diseaseCount: diseases.length,
classificationCount: manualRows.length,
imageClassificationCount: diagnosisCountRows[0]?.value ?? 0,
needsReviewCount: needsReviewRows[0]?.value ?? 0,
riskDistribution,
latestClassification: latestManual && isDiseaseSlug(latestManual.diseaseSlug) && latestDisease ? {
id: latestManual.id,
diseaseSlug: latestManual.diseaseSlug,
observation: latestManual.observation,
location: latestManual.location,
createdAt: latestManual.createdAt.toISOString(),
disease: toDisease(latestDisease),
} : null,
latestDiagnosis,
};
return summary;
} catch (error) {
return serviceUnavailable('Database unavailable');
}
});