Florian WildeandClaude Opus 5 abacad1cb7
Prüfung / pruefung (push) Canceled after 0s
Prüfung / pruefung (pull_request) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s
Prüfung / e2e (pull_request) Canceled after 0s
feat(vorlagen): Versionierung, Unveraenderlichkeit und JSON-Austausch
Eine veroeffentlichte Vorlagen-Version ist fuer immer unveraenderlich — daran
haengt, dass ein archivierter Lauf in zehn Jahren noch genau die Schritte zeigt,
mit denen er gestartet wurde. Die Regel steht deshalb zweimal: im Service, der
die verstaendliche Meldung gibt, und in der Datenbank, die die Garantie gibt.
Vier Trigger fangen Schreibzugriffe ab, die nicht durch den Service kommen —
Migrationen, psql, der Seed und ab Phase 4 die Connectoren — und versperren den
Rueckweg nach ENTWURF, der alles andere ausgehebelt haette.

- Service: anlegen, Kopfdaten bearbeiten, Entwurf als tiefe Kopie der letzten
  veroeffentlichten Version, speichern, veroeffentlichen, verwerfen,
  duplizieren, archivieren
- Hoechstens ein Entwurf je Vorlage, durchgesetzt vom partiellen Index; zwei
  gleichzeitige Klicks auf „Bearbeiten“ bekommen denselben Entwurf statt eines
  Fehlers
- Optimistisches Sperren ueber geaendertAm: wer auf einem ueberholten Stand
  speichert, wird abgewiesen statt die Arbeit des anderen spurlos zu ersetzen
- JSON-Export und -Import samt Format-Dokumentation; der Export ist nachsichtig,
  der Import streng
- src/lib/validierung.ts als einzige Zod-Quelle mit deutschen Meldungen, per
  ESLint-Regel erzwungen

Der unabhaengige Review fand drei Blocker, alle an der Mandantengrenze: der
Suchfilter brachte ein zweites OR mit und ueberschrieb die Kundenbedingung,
Historie und Aufbau einer Version filterten gar nicht. Zu queries/vorlagen.ts
gab es keinen Test — jetzt gibt es tests/vorlagen-sicht.test.ts.

428 Tests gruen (vorher 336).

Schliesst #009

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 08:09:21 +02:00

laufzettel

Checklisten für wiederkehrende IT-Arbeiten. Versionierte Vorlagen, daraus gestartete Läufe, im Team abgearbeitet, abgeschlossen und unveränderlich archiviert — mit Historie je Gerät und je Mitarbeiter. Einstieg über QR-Aufkleber: Code scannen, Geräteseite öffnen, loslegen.

Stand: in Entwicklung auf v0.1.0 (MVP). Der Fortschritt steht in docs/issues/README.md, Entscheidungen im Entwicklungstagebuch.

Zwei Betriebsmodi

Modus Für wen Kunden-Ebene Verzeichnis
INTERN (Standard) eine Firma, eigene Mitarbeiter unsichtbar eigenes AD per LDAP
DIENSTLEISTUNG IT-Dienstleister mit mehreren Kunden sichtbar manuell oder per CSV je Kunde

Der Modus liegt in der Datenbank und ist vom Admin ohne Neustart umschaltbar. Jede kundenbezogene Zeile trägt immer eine Kunden-Zuordnung, im Modus INTERN die eigene Organisation — deshalb ist der Wechsel eine Datenänderung und keine Migration.

Voraussetzungen

  • Node 24 (.nvmrc) und pnpm 12 über corepack, oder nur Docker mit Compose-Plugin
  • PostgreSQL 17 (im Compose-Stand enthalten)

Entwicklung

corepack enable pnpm
pnpm install
cp .env.example .env          # APP_SECRET und Passwörter ausfüllen
pnpm db:up                    # PostgreSQL auf 127.0.0.1:5433
pnpm db:migrate
pnpm db:seed
pnpm dev                      # http://localhost:3000

APP_SECRET erzeugen:

openssl rand -base64 48

Qualitätsschranken

pnpm lint && pnpm typecheck && pnpm test && pnpm pruefe:doku

Ein Pre-Commit-Hook erzwingt Lint und Format, ein Commit-Msg-Hook Conventional Commits. Ende-zu-Ende-Tests: pnpm test:e2e.

Betrieb mit Docker Compose

cp .env.example .env          # APP_URL, APP_SECRET, POSTGRES_PASSWORD sind Pflicht
docker compose up -d --build

Die Migrationen laufen beim Start automatisch in einem eigenen Einmal-Dienst. Der Seed läuft absichtlich nicht automatisch — ihn startest du einmal von Hand:

docker compose run --rm migrate pnpm db:seed

Die Datenbank ist von außen nicht erreichbar

Das ist bewusst so gebaut und in tests/betriebsstand.test.ts festgehalten:

  • postgres hat kein ports:-Mapping, nur expose
  • postgres hängt ausschließlich im Netz intern, das internal: true ist — ein Netz ohne Gateway, also weder vom Host noch aus dem LAN erreichbar
  • nur web hängt zusätzlich im Netz extern (Reverse Proxy, OIDC, LDAP)
  • web bindet standardmäßig auf 127.0.0.1:3000; davor gehört ein Reverse Proxy

Prüfen:

nc -z localhost 5432 && echo "FEHLER: offen" || echo "dicht"
docker compose exec web nc -z postgres 5432 && echo "von innen erreichbar"

Für die Entwicklung gibt es docker-compose.dev.yml, das PostgreSQL auf 5433 öffnet. Diese Datei ist nicht für den Betrieb gedacht.

Reverse Proxy

Die App läuft hinter einem Reverse Proxy und hat keine fest verdrahteten Adressen. Maßgeblich ist APP_URL — sie steckt in den QR-Codes und in der OIDC-Rückruf-Adresse. Der Proxy muss X-Forwarded-Proto und X-Forwarded-Host setzen.

Sicherung

Der geschlossene Port ändert nichts an der Sicherung, sie läuft über den Dienstnamen:

docker compose exec -T postgres pg_dump -U laufzettel laufzettel | gzip > sicherung-$(date +%F).sql.gz

Zurückspielen:

gunzip -c sicherung-2026-09-24.sql.gz | docker compose exec -T postgres psql -U laufzettel laufzettel

Gesichert werden muss nur die Datenbank — die App hält keinen veränderlichen Zustand auf der Platte. Das Volume heißt laufzettel_datenbank.

Anmeldung

Zwei Wege, beide gleichzeitig möglich:

  • OIDC (Ziel: authentik) für den normalen Betrieb
  • lokal mit E-Mail und Passwort, nur wenn AUTH_LOCAL_ENABLED=true — für die Entwicklung und als Notfallzugang, wenn der Identitätsanbieter ausfällt

Die Rolle kommt bei jedem OIDC-Login aus dem groups-Claim und wird über OIDC_GROUP_ADMIN, OIDC_GROUP_EDITOR, OIDC_GROUP_TECHNICIAN und OIDC_GROUP_VIEWER zugeordnet. Unbekannte Benutzer werden beim ersten Login angelegt.

Rolle Darf
ADMIN alles, inkl. Benutzer, Verzeichnis, Abteilungen, Archiv-Verwaltung
REDAKTEUR Vorlagen anlegen, bearbeiten, veröffentlichen; alles was TECHNIKER darf
TECHNIKER Läufe starten, abarbeiten, abschließen, archivieren; Geräte pflegen
LESER nur lesen

Die Anbindung an authentik ist in docs/betrieb/authentik.md Schritt für Schritt beschrieben.

Umgebungsvariablen

Alle Variablen mit Erklärung stehen in .env.example. Pflicht sind APP_URL, APP_SECRET, POSTGRES_PASSWORD und DATABASE_URL; der Compose-Stand bricht ab, statt mit Standardwerten zu starten.

Dokumentation

Lizenz

Noch nicht festgelegt.

S
Description
No description provided
Readme
27 MiB
2026-10-02 22:17:05 +00:00
Languages
TypeScript 96.1%
Shell 2.4%
PLpgSQL 0.8%
JavaScript 0.5%
CSS 0.2%