API-first: OpenAPI 3.0-spec för plattforms-API:t

- services/plattform/openapi.yaml dokumenterar hela ytan: auth
  (registrera organisation, logga in), användarhantering (admin),
  ärenden + append-only händelselogg (idempotent synk), arbetsledar-
  översikten, publik Live Share-delning och AI-orkestern — inklusive
  scheman för alla 15 händelsetyper, roller, JWT-anspråken och
  API:ts bärande principer (append-only, multi-tenant-404).
- Specen serveras live av plattformstjänsten på GET /api/openapi.yaml
  och följer med i containern.
- Verifierad i tre lager: maskinell validering (swagger-cli),
  paritetstest i vitest (varje dokumenterad väg finns i servern,
  händelsetyperna är kompletta) och integrationsteststeg som hämtar
  specen från den körande tjänsten.

Verifierat: integrationstestets 18 kontroller gröna mot Postgres 16,
29 vitest-tester gröna, produktionsbygge ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
This commit is contained in:
Claude
2026-08-03 07:22:05 +00:00
parent 04ab80a0b0
commit 12c5f1c617
7 changed files with 516 additions and 4 deletions
+13
View File
@@ -16,8 +16,14 @@
import { createServer } from "node:http";
import { createHmac, timingSafeEqual } from "node:crypto";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
import pg from "pg";
// API-first: OpenAPI-specen är en versionerad artefakt och serveras live.
const OPENAPI = readFileSync(join(dirname(fileURLToPath(import.meta.url)), "openapi.yaml"), "utf8");
const PORT = Number(process.env.PORT ?? 8080);
const MAX_KROPP = 4 * 1024 * 1024;
const TOKEN_LIVSTID_S = 12 * 60 * 60;
@@ -117,6 +123,13 @@ export function skapaServer() {
if (req.method === "GET" && vag === "/halsa") {
return svara(res, 200, { status: "ok" });
}
if (req.method === "GET" && vag === "/api/openapi.yaml") {
res.writeHead(200, {
"Content-Type": "application/yaml; charset=utf-8",
"Access-Control-Allow-Origin": "*",
});
return res.end(OPENAPI);
}
const hemlighet = process.env.JWT_SECRET;
if (!hemlighet) return svara(res, 503, { error: "Tjänsten är inte konfigurerad." });