Neben "Drucken" steht am Lauf "Als PDF", im Archiv an jedem Treffer "Protokoll als PDF" — dasselbe Blatt als fertige Datei, zum Verschicken oder Abheften. Unten darauf jetzt zwei Unterschriftsfelder mit Zeile fuer Ort, Datum und Namen; wo eine Vier-Augen-Freigabe erteilt wurde, steht sie mit im Kopf. Erzeugt wird es, indem der Server seine eigene Druckansicht ueber 127.0.0.1 laedt und druckt — mit dem Sitzungscookie des Anfragenden. Damit gibt es genau eine Vorlage fuer Papier und Datei, und fuer das PDF gilt dieselbe Rechte- und Mandantenpruefung wie fuer die Seite. Ein zweites Layout waere billiger und wuerde frueher oder spaeter abweichen, ohne dass es jemandem auffaellt. Ein eigener Browser-Kontext je PDF, kein geteilter: er traegt die fremde Sitzung, und ein geteilter gaebe sie an das naechste PDF weiter. Gespeichert wird nichts — ein abgelegtes PDF waere eine zweite Wahrheit neben dem Lauf. Ein erzeugtes Passwort steht auch hier nie darauf, geprueft am extrahierten Text des fertigen PDF und nicht an den Bytes: der Inhalt ist komprimiert, dort nichts zu finden bewiese nichts. Chromium bleibt optional, und die gemessene Zahl ist der Grund: das Laufzeitbild waechst von 370 MB auf 1,52 GB, Alpines chromium kostet allein 1,13 GB. Er kommt nur mit `--build-arg MIT_PDF=1` beziehungsweise `deploy/install.sh --mit-pdf` mit. Fehlt er, erscheint der Knopf gar nicht erst — ein Knopf, der zuverlaessig in eine Fehlermeldung fuehrt, ist schlimmer als keiner. Ein direkter Aufruf bekommt 503 mit einer Meldung, die sagt, was fehlt und wo es steht; ein leeres PDF gibt es in keinem Fall. Meine Schaetzung lag um das Dreieinhalbfache daneben: "+328 MB" war das .apk-Paket, nicht das Bild, und stand schon im Dockerfile-Kommentar, bevor ich gebaut hatte. Jetzt steht die gemessene Zahl an drei Stellen. 1545 Vitest-Tests (vorher 1526): 15 in tests/pdf.test.ts, die den Handler echt fahren, 3 in tests/pdf-grenze.test.ts fuer die Obergrenze mit gestelltem Browser, einer in tests/archiv.test.ts. Dazu drei E2E-Ablaeufe; volle Suite gruen mit 118 bestanden. Ein Blatt wurde wirklich erzeugt und angesehen. Sechs Gegenproben, alle angeschlagen — darunter ein echtes Passwort auf dem Blatt und die Freigabe des Zaehlers aus dem finally genommen, der Fall, in dem ein Fehlschlag die PDF-Erzeugung bis zum Neustart leise stilllegt. Closes #41 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
141 lines
6.1 KiB
Bash
141 lines
6.1 KiB
Bash
# Laufzettel – Umgebungsvariablen.
|
||
# Kopieren nach .env und ausfuellen. Die .env gehoert nie ins Repo.
|
||
|
||
# ---------- Pflicht ----------
|
||
|
||
# Oeffentliche Adresse der Anwendung. Steckt in den QR-Codes und in der
|
||
# OIDC-Rueckruf-Adresse, muss also stimmen, wenn ein Reverse Proxy davor haengt.
|
||
APP_URL=https://laufzettel.example.de
|
||
|
||
# Geheimnis fuer Sitzungs-Cookies und die Verschluesselung des LDAP-Bind-Passworts.
|
||
# Mindestens 32 Zeichen. Erzeugen mit: openssl rand -base64 48
|
||
APP_SECRET=
|
||
|
||
# Zugang zur Datenbank.
|
||
#
|
||
# Im Compose-Stand baut sich die Adresse aus den drei POSTGRES_*-Werten zusammen:
|
||
# die Datenbank heisst dort `postgres` (Dienstname im internen Netz) und ist von
|
||
# aussen nicht erreichbar. DATABASE_URL wird dafuer **nicht** gelesen.
|
||
#
|
||
# DATABASE_URL gilt fuer alles, was ausserhalb der Container laeuft: `pnpm dev`,
|
||
# `pnpm prisma …`, `pnpm db:seed` von der Werkbank aus. Standard ist die
|
||
# ---- Anhaenge (#032) -------------------------------------------------------
|
||
# Wo die Dateien der Anhaenge liegen. Im Compose-Stand setzt docker-compose.yml
|
||
# das selbst auf /daten/anhaenge im Volume `anhaenge`; hier gilt es fuer die
|
||
# Entwicklung und die native Installation.
|
||
#
|
||
# **Gehoert in jede Sicherung.** In der Datenbank stehen nur die Metadaten.
|
||
ANHAENGE_PFAD=./daten/anhaenge
|
||
# Hoechstgroesse je Datei in MB und Hoechstzahl Anhaenge je Lauf.
|
||
ANHANG_MAX_MB=10
|
||
ANHAENGE_JE_LAUF=20
|
||
#
|
||
# **Wer das anhebt, hebt `client_max_body_size` im Reverse Proxy mit an.** Sonst
|
||
# weist nginx die Datei mit 413 ab, bevor die Anwendung ihre eigene, freundliche
|
||
# Meldung zeigen kann — siehe docs/betrieb/reverse-proxy.md.
|
||
|
||
# ---- Live-Aktualisierung (#033) --------------------------------------------
|
||
# Die Abarbeitungsansicht haelt einen Ereignisstrom offen, statt alle zehn
|
||
# Sekunden zu fragen. Beide Werte brauchen im Normalfall niemand anzufassen.
|
||
#
|
||
# Abstand der Herzschlaege in Millisekunden. Muss **unter** der Leerlaufzeit des
|
||
# Reverse Proxy liegen (nginx und Caddy: 60 s), sonst schliesst der die stille
|
||
# Verbindung.
|
||
SSE_HERZSCHLAG_MS=25000
|
||
# Hoechstzahl gleichzeitig offener Stroeme je Instanz. Ist sie erreicht, fallen
|
||
# weitere Laschen von sich aus auf das Polling zurueck — die Seite bleibt
|
||
# bedienbar, nur nicht mehr sofort aktuell.
|
||
SSE_MAX_STROEME=200
|
||
|
||
# Entwicklungsdatenbank aus docker-compose.dev.yml (Port 5434, umstellbar ueber DEV_DB_PORT).
|
||
POSTGRES_USER=laufzettel
|
||
POSTGRES_PASSWORD=
|
||
POSTGRES_DB=laufzettel
|
||
DATABASE_URL=postgresql://laufzettel:laufzettel@localhost:5434/laufzettel?schema=public
|
||
|
||
# ---------- Anmeldung ----------
|
||
|
||
# Lokaler Login mit E-Mail und Passwort. Fuer die Entwicklung und als Notfallzugang.
|
||
# Im Betrieb mit OIDC auf false setzen.
|
||
AUTH_LOCAL_ENABLED=false
|
||
|
||
# Generischer OIDC-Anbieter, Ziel ist authentik. Leer lassen, um OIDC abzuschalten.
|
||
OIDC_ISSUER=
|
||
OIDC_CLIENT_ID=
|
||
OIDC_CLIENT_SECRET=
|
||
OIDC_NAME=authentik
|
||
# Angeforderte Bereiche. Der Standard reicht fuer authentik mit dem
|
||
# Gruppen-Mapping aus docs/betrieb/authentik.md. Nur aendern, wenn der Anbieter
|
||
# die Gruppen unter einem anderen Bereich liefert.
|
||
OIDC_SCOPE=openid profile email groups
|
||
|
||
# Zuordnung der Rollen ueber den groups-Claim. Wird bei jedem Login neu ausgewertet.
|
||
OIDC_GROUP_ADMIN=lz-admin
|
||
OIDC_GROUP_EDITOR=lz-redakteur
|
||
OIDC_GROUP_TECHNICIAN=lz-techniker
|
||
OIDC_GROUP_VIEWER=lz-leser
|
||
|
||
# Optional, nur im Betriebsmodus Dienstleistung: Gruppen wie lz-kunde-MUS binden
|
||
# Benutzer an Kunden. TECHNIKER und LESER sehen dann nur ihre Kunden.
|
||
# Leer lassen heisst: keine Einschraenkung.
|
||
OIDC_CUSTOMER_GROUP_PREFIX=
|
||
|
||
# ---------- Betrieb ----------
|
||
|
||
# Adresse, auf die der Webdienst gebunden wird. Standard ist nur localhost,
|
||
# davor gehoert ein Reverse Proxy. Fuer direkten Zugriff im LAN: 0.0.0.0
|
||
BIND_ADDR=127.0.0.1
|
||
APP_PORT=3000
|
||
|
||
# Wird beim Bauen in die Anzeige unten links uebernommen.
|
||
APP_VERSION=0.1.0
|
||
GIT_SHA=
|
||
|
||
# ---------- Seed ----------
|
||
|
||
SEED_ADMIN_EMAIL=admin@firma.example
|
||
SEED_ADMIN_PASSWORD=
|
||
# INTERN (Standard) oder DIENSTLEISTUNG. SERVICE wird als Alias akzeptiert.
|
||
SEED_MODE=INTERN
|
||
|
||
# Name der eigenen Organisation. Im Modus INTERN taucht er nirgends in der
|
||
# Oberflaeche auf, wird aber beim Wechsel zu DIENSTLEISTUNG zum ersten Kunden.
|
||
SEED_ORG_NAME=Eigene Organisation
|
||
# 2 bis 6 Grossbuchstaben oder Ziffern. Im Modus INTERN unsichtbar; im Modus
|
||
# DIENSTLEISTUNG steht das Kuerzel auf Etiketten und in der Hostname-Regel.
|
||
SEED_ORG_KUERZEL=EIGEN
|
||
|
||
# ---------- Verzeichnis-Abgleich ----------
|
||
|
||
# Der Zeitplan laeuft im selben Prozess wie die Anwendung und sieht jede Minute
|
||
# nach, welche Verzeichnisquelle faellig ist; das Intervall steht je Quelle in
|
||
# der Datenbank. `false` schaltet ihn ab — fuer Einmal-Container, die nur
|
||
# Migrationen fahren, und fuer Testlaeufe.
|
||
ZEITPLAN_AKTIV=true
|
||
|
||
# ---- PDF-Uebergabeprotokoll (#041) -----------------------------------------
|
||
# Das Protokoll eines Laufs als PDF entsteht, indem der Server seine **eigene**
|
||
# Druckansicht mit Chromium laedt. Dafuer muss Chromium da sein – und das ist er
|
||
# absichtlich nicht ueberall: im Bild kostet er 1,13 GB, mehr als die dreifache
|
||
# Anwendung. Ohne ihn zeigt die Oberflaeche den Knopf gar nicht erst, und die
|
||
# Druckansicht bleibt der Weg. Alles dazu in docs/betrieb/pdf.md.
|
||
#
|
||
# Leer lassen heisst „suche selbst“: /usr/bin/chromium-browser (Alpine),
|
||
# /usr/bin/chromium, /usr/bin/google-chrome. Nur setzen, wo er woanders liegt –
|
||
# ein Pfad, der ins Leere zeigt, ist ein Fehler und kein Rueckfall auf die Suche.
|
||
CHROMIUM_PFAD=
|
||
# Wie viele PDFs gleichzeitig entstehen duerfen. Jedes haelt kurz einen
|
||
# Browser-Kontext offen; mehr als eine Handvoll bringt auf kleinen Maschinen nur
|
||
# Arbeitsspeicherdruck. Darueber hinaus bekommt der Anwender die Bitte, es gleich
|
||
# noch einmal zu versuchen.
|
||
PDF_MAX_GLEICHZEITIG=2
|
||
# Nach wie vielen Millisekunden Leerlauf der Chromium wieder schliesst. Er haelt
|
||
# offen rund 100 MB belegt – auf einem 2-GB-LXC ist das spuerbar.
|
||
PDF_LEERLAUF_MS=60000
|
||
|
||
# ---------- Tests ----------
|
||
|
||
# Datenbank fuer Vitest. Ohne diese Variable ueberspringen sich die Datenbanktests.
|
||
# Passend zu docker-compose.dev.yml (Port 5434, umstellbar ueber DEV_DB_PORT):
|
||
TEST_DATABASE_URL=postgresql://laufzettel:laufzettel@localhost:5434/laufzettel_test?schema=public
|