SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
5 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
2.4 KB · 33 lines typescript
Raw Blame History
1/** Admin data-quality dashboard: overview, flag list, resolve / dismiss. */2import type { FastifyInstance } from "fastify";3import { z } from "zod";4import { envelope, notFound, parseBody, parseQuery } from "../../lib/http.js";5import { intParam, pageParam, strParam } from "../../lib/params.js";6import { listFlags, qualityOverview, setFlagStatus } from "../../repositories/admin/quality.js";78export async function qualityAdminRoutes(app: FastifyInstance): Promise<void> {9  app.get("/quality", { schema: { summary: "Data-quality overview (QualityOverview): open flags by severity / code, largest published values, review queue, alerts" } }, async () => envelope(await qualityOverview(), { methodology: "Counts are over open quality_flags, claims by status and live (not hidden / merged) projects. 'Largest' lists are plain ORDER BY on published figures; pipeline MW per operator / metro is containment-aware (campus vs buildings never both) plus site-scoped project MW." }));1011  app.get("/quality/flags", { schema: { summary: "Quality flags (QualityFlagDTO[]) ?status=open|resolved|dismissed|all&code=&entity_type=&min_priority=&page=&per_page=" } }, async (req) => {12    const q = parseQuery(z.object({ status: strParam, code: strParam, entity_type: strParam, min_priority: intParam, page: pageParam, per_page: intParam }), req.query);13    const res = await listFlags({ status: q.status ?? "open", code: q.code, entityType: q.entity_type, minPriority: q.min_priority, page: q.page, perPage: q.per_page });14    return envelope(res.items, { total: res.total, page: res.page, perPage: res.perPage });15  });1617  app.post("/quality/flags/:id/resolve", { schema: { summary: "Resolve a quality flag {resolution}" } }, async (req) => {18    const { id } = req.params as { id: string };19    const body = parseBody(z.object({ resolution: z.string().min(1).max(500) }), req.body);20    const flag = await setFlagStatus(id, "resolved", body.resolution);21    if (!flag) throw notFound("quality flag");22    return envelope(flag);23  });2425  app.post("/quality/flags/:id/dismiss", { schema: { summary: "Dismiss a quality flag {reason?}" } }, async (req) => {26    const { id } = req.params as { id: string };27    const body = parseBody(z.object({ reason: z.string().max(500).optional() }), req.body ?? {});28    const flag = await setFlagStatus(id, "dismissed", body.reason ?? null);29    if (!flag) throw notFound("quality flag");30    return envelope(flag);31  });32}33