Files
Cibello-app/apps/api/src/routes/health.ts
T
Sven (AAMOS AI) 6e2d794130 feat(storage,deploy): delad S3/mock-lagring, SSM .env-sync, Gemini readyz
- @app/storage med MockStorage + AwsStorage delas mellan API och worker

- Workerns readUrl() ger presignerad S3-URL i aws-läge (fixar mock-s3 404)

- sync-env-from-ssm.py hämtar /cibello/prod/* och skriver .env; first-deploy.sh kör det i prod

- /readyz returnerar 503 om app.aamos.healthCheck() misslyckas

- docs/SECRETS.md med rotations- och SSM-regler
2026-08-12 17:28:58 +07:00

45 lines
2.0 KiB
TypeScript

import type { FastifyInstance } from "fastify";
import { sql } from "drizzle-orm";
import { BRAND } from "@app/shared-types";
/** Liveness/readiness för lastbalanserare och Docker healthchecks (spec §58). */
export async function healthRoutes(app: FastifyInstance) {
app.get("/healthz", { config: { rateLimit: false } }, async () => ({
ok: true,
service: `${BRAND.slug}-api`,
timestamp: new Date().toISOString(),
}));
app.get("/readyz", { config: { rateLimit: false } }, async (_req, reply) => {
try {
await app.db.execute(sql`SELECT 1`);
} catch {
return reply.status(503).send({ ok: false, reason: "database" });
}
const aamosHealth = await app.aamos.healthCheck();
if (!aamosHealth.ok) {
return reply.status(503).send({ ok: false, reason: "aamos", detail: aamosHealth.detail });
}
return { ok: true };
});
/** Enkel endpointöversikt i stället för tung OpenAPI-generering (se beslutslogg D-011). */
app.get("/docs", { config: { rateLimit: false } }, async (_req, reply) => {
const routes = app.routeCatalog
.filter((r) => !r.url.startsWith("/v1/mock-s3"))
.sort((a, b) => a.url.localeCompare(b.url) || a.method.localeCompare(b.method));
const rows = routes
.map((r) => `<tr><td><code>${r.method}</code></td><td><code>${r.url}</code></td></tr>`)
.join("\n");
reply.header("content-type", "text/html; charset=utf-8");
return `<!doctype html><html lang="sv"><head><meta charset="utf-8"><title>API</title>
<style>body{font-family:system-ui;margin:2rem auto;max-width:820px;color:#1a202c;padding:0 1rem}
table{border-collapse:collapse;width:100%}td{padding:.35rem .75rem;border-bottom:1px solid #e2e8f0}
code{background:#f7fafc;padding:.1rem .3rem;border-radius:4px}</style></head>
<body><h1>${BRAND.name} API v1</h1>
<p>${routes.length} endpoints. Auth: <code>Authorization: Bearer &lt;accessToken&gt;</code>.
Fullständig referens: <code>docs/api-referens.md</code> i repot.</p>
<table>${rows}</table></body></html>`;
});
}