Harlekin bd750f26d0
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
Statuswechsel als Karte statt als wandernde Auswahlliste
Das Auswahlfeld enthielt nur die gerade erreichbaren Zustaende. Damit sah
man weder, wo die Akte steht, noch welche Zustaende es ueberhaupt gibt - und
nach jedem Wechsel standen andere Eintraege darin. Eine Auswahl, die ihre
Eintraege tauscht, ist keine Orientierung.

Jetzt stehen immer alle sieben da, in der Reihenfolge des Lebenszyklus:
Entwurf, Offen, In Pruefung, Abgeschlossen, Archiviert als eine Linie,
darunter Ruhend und Versiegelt als Nebenwege. Der jetzige Zustand ist
markiert, erreichbare sind Knoepfe, gesperrte bleiben blass stehen statt zu
verschwinden - mit dem Grund als Titel daran.

Der Grund ist dabei nicht dasselbe wie die Sperre: "von hier fuehrt kein
Weg dorthin" laesst sich durch kein Recht beheben, "dir fehlt case.close"
schon. Wer das verwechselt, sucht an der falschen Stelle.

Was ein Wechsel verlangt, steht jetzt vorher da statt als Absage danach:
die Pflichtbegruendung beim Wiedereroeffnen und beim Versiegeln, und dass
vor dem Abschluss die noetigen Abschnitte gefuellt sein muessen. Bei
Pflichtbegruendung bleibt der Knopf gesperrt, bis sie dasteht.

Die Rechtepruefung rechnet der Server, mit derselben Funktion, die der
Dienst beim Wechsel anwendet - vorher zeigte die Liste auch Ziele, die
danach abgelehnt wurden. Zwei Pruefungen halten das fest, und zwar in beide
Richtungen: nichts angeboten, was scheitern wuerde, und nichts verschwiegen,
was ginge. Die zweite ist die wichtigere, weil ihr Fehlen nicht auffaellt.
2026-09-04 16:26:29 +02:00
2026-09-01 00:01:49 +02:00
2026-09-01 00:01:29 +02:00
2026-09-01 00:01:29 +02:00
2026-09-01 00:01:29 +02:00

AfiS-Aktensystem

Fall- und Personalaktenverwaltung für die Abteilung für innere Sicherheit eines SCP-RP-Servers. Anmeldung und Berechtigungen laufen vollständig über Discord.

Interne Anwendung für eine geschlossene Gruppe von zehn bis fünfzig Personen, selbst gehostet. Keine Mandantentrennung, keine öffentliche Registrierung.

Was es kann

  • Fallakten mit Kopfdaten, Abschnitten nach Vorlage und einem Verlauf, der nur wächst
  • Personalakten, überwiegend für Externe, mit Aktenblatt zum Ausdrucken
  • Abteilungen als Ebene darüber: was zu ihnen aktenkundig ist, ergibt sich aus ihren Mitgliedern
  • Dokumente nach Vorlage, an einer Fall- oder Personalakte
  • Fünfstufige Klassifizierung mit feldgenauer Schwärzung
  • Volltextsuche je Freigabestufe, Kommandopalette auf Strg+K
  • Chat in jeder Akte, mit Übernahme einer Nachricht als Vermerk
  • Export als PDF mit Wasserzeichen, jeder Abruf protokolliert
  • Zugriffsprotokoll über jede schreibende Aktion und jeden Lesezugriff ab Stufe 3

Zusicherungen

Acht Regeln, die im Code und wo möglich in der Datenbank durchgesetzt sind. Sie sind der Grund, warum das System so gebaut ist, wie es gebaut ist - Codekommentare verweisen unter ihrer Nummer hierher.

  1. Berechtigungsprüfung immer serverseitig und immer in der Datenbankabfrage. Niemals alle Datensätze laden und danach filtern.
  2. Verlaufseinträge dürfen nachträglich geändert werden, aber niemals unsichtbar. Jede Änderung erzeugt eine Version, jede Zurücknahme lässt einen Grabstein stehen, beides ist für alle sichtbar, die den Eintrag sehen dürfen. Es gibt kein DELETE - das steht als Regel in der Datenbank, nicht nur in der Anwendung.
  3. Jede schreibende Aktion und jeder Lesezugriff ab Stufe 3 erzeugt einen Protokolleintrag.
  4. Keine Dateiendungen als Typprüfung. Immer Magic Bytes. Kein SVG, kein HTML, keine Archive.
  5. Keine Blockade-Spinner. Jede Ansicht hat ein Skelett, jede Änderung eine sofortige Rückmeldung.
  6. Existenz ist auch eine Information. Akten oberhalb der Sichtstufe tauchen weder in Listen noch in Trefferzahlen noch in Rückverweisen auf. Kein Zugriff heisst 404, nicht 403.
  7. Keine Geheimnisse im Repository. Alles über .env, Vorlage in .env.example.
  8. Keine Einmal-Container. Der Betrieb läuft über Dockge, das beendete Container nicht dauerhaft anzeigt. Migrationen gehören ins Startkommando von web, wiederkehrende Aufgaben in Dauerläufer mit eigenem Cron. Jeder Dienst braucht einen Healthcheck, weil das in Dockge die Statusanzeige ist.

Aufbau

web/           Next.js 15, App Router, React 19
worker/        discord.js, gleicht Rollen und Mitgliedschaft ab
pdf/           Playwright, erzeugt die PDF-Ausdrucke
packages/db/   Prisma-Schema, Migrationen, Seed
docker/        Dockerfiles für web, worker und pdf
compose.yaml   Der Dockge-Stack
.env.example   Vorlage für den .env-Reiter in Dockge

Stack: Next.js 15 mit React 19 und TypeScript, Auth.js v5 mit Discord-Provider, Prisma auf PostgreSQL 16, Tailwind ohne Komponentenbibliothek. ClamAV und Playwright laufen als eigene Container.

Einrichten

Discord-Application anlegen, Bot auf den Server einladen und Server Members Intent aktivieren - ohne das liefert der Bot keine Rollen.

Lokal entwickeln

Node 22 und pnpm 9.

corepack enable && pnpm install

docker run -d --name afis-db -e POSTGRES_DB=afis -e POSTGRES_USER=afis \
  -e POSTGRES_PASSWORD=afis -p 5434:5432 postgres:16-alpine

cp .env.example .env
# DATABASE_URL und DIRECT_DATABASE_URL auf localhost:5434 zeigen lassen

pnpm --filter @afis/db run build   # erzeugt den Prisma-Client
pnpm db:deploy && pnpm db:seed
pnpm dev

Prüfen wie die CI:

pnpm lint && pnpm typecheck && pnpm test
pnpm test:e2e

pnpm test legt sich eine eigene Datenbank afis_test an und lässt die Entwicklungsdatenbank in Ruhe: Verlaufseinträge und Protokollzeilen sind nach Regel 2 nicht löschbar, was ein Test dort anlegt, bliebe für immer stehen.

Betrieb

Gehostet als ein Dockge-Stack. Die .env wird nicht im Repository gepflegt, sondern im .env-Reiter von Dockge.

Verwaltung Dockge, ein Stack namens afis unter /opt/stacks/afis/
Erreichbarkeit Cloudflare Tunnel, kein offener Port am Router
Aktualisieren Dockge → Stack → Update, Migration läuft automatisch mit
Backup zwei Dauerläufer mit Cron: nächtlicher Dump plus restic offsite
Wiederherstellung scripts/restore-test.sh spielt einen Dump in eine Wegwerf-Datenbank und vergleicht Zeilenzahlen, Regeln und Trigger
S
Description
Aktensystem für die AfiS aus dem SCP-RP, mit Discord-Synchro
Readme
1.4 MiB
Languages
TypeScript 91.4%
PLpgSQL 4.5%
CSS 2.3%
JavaScript 0.7%
Shell 0.6%
Other 0.5%