Zum Inhalt

Struktur & Stack

Verzeichnisstruktur

app/
  (public)/            Öffentliche Statusseite, Historie, Service-Detail, Badge,
                       Rechtstexte, Abonnement-Bestätigung, Abmeldung, My data
  admin/
    login/             Anmeldeseite (ohne Guard)
    (dashboard)/       Geschützte Admin-Seiten mit Navigations-Layout
      sla-reports/     SLA-Reports; Route Handler [id]/pdf, [id]/xlsx und adhoc
  api/
    status/            Öffentliche Status-API (JSON)
    health/            Infrastruktur-Health-Check
    outage/            Statusänderungs-Routen für API-Tokens
    incidents/         Incident per API-Token anlegen
    maintenances/      Wartungsfenster per API-Token anlegen
  api-docs/            Swagger UI für das OpenAPI-Dokument
  badge.svg/           Dynamisches SVG-Statusbadge
  feed.xml/            RSS-Feed der Incidents
components/            UI der Statusseite und Admin-Bausteine
components/ui/         shadcn/ui-Komponenten (Base UI)
instrumentation.ts     Startprüfung beim Serverstart (SESSION_SECRET)
lib/
  actions/             Server Actions (liefern ActionResult<T>)
  api/                 JSON-Antwort-Helfer für API-Routen
  audit/               Schreiben und Kapseln des Audit-Logs
  auth/                Passwörter, Sessions, Rollen-Guards, API-Token-Prüfung, Rate-Limiting
  crypto/              AES-256-GCM für Geheimnisse
  db/
    schema.ts          Drizzle-Schema und Enum-Konstanten (Quelle der Domänentypen)
    queries/           Datenbankzugriffe
    seed.ts            Demodaten
  domain/              Fachlogik (Status, Uptime, SLA, Zeitzonen, Rollen)
  email/               E-Mail-Templates, Transporte, Processor
  env/                 Prüfung von Umgebungsvariablen beim Start
  export/              SLA-Export: Laden des Kontexts, Excel-Mappe, Antwort, Texte en/de
  notifications/       Dispatch, Queues, Scheduler, Uptime-Checker
  pdf/                 Erzeugung der SLA-Report-PDFs
  push/ sms/ webhooks/ Kanalspezifische Transporte, Payload-Builder, Processor
  validations/         Zod-Schemas
  viewer-timezone.ts   Zeitzonen-Umschalter der Besucher (Logik und Store)
i18n/                  next-intl-Konfiguration
messages/              en.json und de.json
scripts/               Worker, Migrationen, Backup/Restore, Seeding, Hilfsskripte
drizzle/               SQL-Migrationen und drizzle-kit-Metadaten
tests/                 Unit-Tests, Integrationstests, Factories
e2e/                   Playwright-Specs
public/                Statische Dateien, openapi.json, Service Worker für Push

Schichtenregeln

Der Code folgt einer festen Abhängigkeitsrichtung:

  1. Seiten und Routen (Server Components, Route Handler) lesen Daten und rendern. Sie rufen Queries oder Domänenfunktionen auf.
  2. Server Actions (lib/actions/) verarbeiten Formulare. Sie prüfen die Rolle, validieren die Eingabe mit Zod, führen die Query in einer Transaktion aus und rufen revalidatePath auf.
  3. Queries (lib/db/queries/) bündeln die Datenbankzugriffe.
  4. Domäne (lib/domain/) enthält überwiegend reine Funktionen ohne I/O, zum Beispiel Uptime-Berechnung, SLA-Fenster, Schweregrade und Zeitzonenumrechnung. Sie lassen sich ohne Datenbank testen.

Wenige Stellen greifen bewusst direkt auf die Datenbank zu: applyComponentStatusChange (lib/domain/component-status.ts) öffnet die Transaktion für Statusänderungen, der E-Mail-Processor (lib/email/processor.ts) schreibt den Versandstatus, und /api/health setzt ein SELECT 1 ab.

Eine typische Formularübermittlung sieht so aus:

Formular → Server Action → Rollenprüfung (getCurrentUser + ROLES_CAN_*)
         → Zod-Validierung → Query in Transaktion → revalidatePath

Datenfluss der öffentlichen Seite

Seite (Server Component) → getStatusPageData() → Queries → PostgreSQL

getStatusPageData() bündelt alle Queries der öffentlichen Seite. Die Seite selbst bleibt eine dünne Renderschicht.

Konventionen

  • Eingaben aus Formularen und APIs gelten als nicht vertrauenswürdig und werden immer mit Zod geprüft.
  • Logging läuft über den eingebauten Logger lib/log.ts: strukturiertes JSON in Produktion, lesbare Ausgabe in der Entwicklung. console.* wird nur in Error-Boundaries und CLI-Skripten verwendet.
  • Audit-Einträge schreibt logAudit, das nie eine Exception wirft. Ein fehlgeschlagener Audit-Schreibvorgang darf die Fachaktion nicht scheitern lassen.
  • Zeiten werden als timestamptz gespeichert. Die Anzeige folgt settings.timezone und settings.display_timezone, Wandzeit-Eingaben werden sommerzeitsicher umgerechnet (lib/domain/timezone.ts). Im öffentlichen Portal können Besucher auf ihre Browser-Zone umschalten. Die Wahl liegt im Browser (localStorage tz-mode), der Server rendert immer in der App-Zone.
  • Datumsformatierung ohne explizite Locale und Zeitzone (argumentloses toLocale*) ist verboten, weil sie Hydration-Fehler erzeugt. Ein Unit-Test prüft das für app/, components/ und lib/.