feat(betrieb): native Installation, Proxmox-Einzeiler und Abnahme
Zwei Wege in den Betrieb, beide so weit gefahren, wie es hier moeglich war. deploy/install.sh richtet Debian 13 in neun Schritten ein: PostgreSQL 17 hoert nur am Unix-Socket, der Dienstnutzer hat kein Login, die Geheimnisse entstehen im Zielsystem und liegen 0600 bei root. Die Umgebungsdatei wird nie ueberschrieben -- auch nicht beim Aktualisieren, sonst enden alle Sitzungen und die verschluesselten Bind-Passwoerter waeren unlesbar. deploy/proxmox/laufzettel.sh legt den Container an und ruft install.sh darin auf. Der pct-Teil ist ungetestet, weil kein PVE-Host zur Verfuegung stand; die Gegenprobe steht als Pruefliste in docs/betrieb/proxmox.md, das Folge-Issue ist #024. Die Abnahme gegen alle 61 Kriterien hat drei Zusagen ohne Deckung gefunden, alle behoben: der Healthcheck des Webdienstes konnte nie bestehen (busybox loest localhost nach ::1 auf, Next lauscht nur auf IPv4), die CI-Pipeline konnte in keinem Job gruen werden (kein LDAP-Dienst, kein gestarteter Server, kein shellcheck), und der README nannte fuer die Entwicklungsdatenbank den falschen Port. Closes #19
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
# #019 M14 – Betriebswege und Release v0.1.0
|
||||
|
||||
**Status:** offen
|
||||
**Status:** erledigt
|
||||
**Meilenstein:** v0.1.0
|
||||
**Labels:** phase-1, infra, docs
|
||||
**Geschätzt:** 10 h **Tatsächlich:** –
|
||||
**Geschätzt:** 10 h **Tatsächlich:** 5 h 25 min
|
||||
|
||||
## Ziel
|
||||
|
||||
@@ -11,30 +11,30 @@ Zwei erprobte Wege in den Betrieb und ein sauberes erstes Release.
|
||||
|
||||
## Aufgaben
|
||||
|
||||
- [ ] `deploy/install.sh`: native Installation auf Debian 13 — PostgreSQL 17 mit `listen_addresses = ''` (nur Unix-Socket), Node 24 aus dem NodeSource-Repo, corepack/pnpm, Dienstnutzer `laufzettel` ohne Login, Quellcode nach `/opt/laufzettel`, `pnpm install`, `pnpm build`, `prisma migrate deploy`, `prisma db seed`
|
||||
- [ ] Geheimnisse im Zielsystem erzeugen: `APP_SECRET` per `openssl rand -base64 48`, Zufallspasswörter für Datenbank und Admin; Ablage in `/etc/laufzettel/laufzettel.env` mit Modus 0600 und Eigentümer root
|
||||
- [ ] `laufzettel.service`: systemd-Unit für den Next-standalone-Server, `User=laufzettel`, `EnvironmentFile=`, `NoNewPrivileges=yes`, `ProtectSystem=strict`, `PrivateTmp=yes`, `Restart=on-failure`
|
||||
- [ ] Erneuter Aufruf aktualisiert statt neu anzulegen: `git pull`, `pnpm build`, `migrate deploy`, `systemctl restart`; Geheimnisse und Daten bleiben
|
||||
- [ ] `deploy/proxmox/laufzettel.sh`: One-Liner für die Proxmox-Shell — `set -Eeuo pipefail`, `trap` mit Zeilennummer, farbige Ausgabe, Spinner, Prüfung auf PVE 8/9 und root
|
||||
- [ ] `whiptail`-Dialog Standard gegen Erweitert; Standard: nächste freie CTID, Hostname `laufzettel`, Debian 13, 2 Kerne, 2 GB RAM, 8 GB Platte, unprivilegiert, DHCP auf `vmbr0`
|
||||
- [ ] Debian-Vorlage bei Bedarf per `pveam` holen, `pct create`, starten, `deploy/install.sh` im Container ausführen
|
||||
- [ ] `--dry-run`: gibt alle `pct`-Aufrufe nur aus, ohne etwas anzulegen
|
||||
- [ ] Abschlussbildschirm: URL, Admin-Kennung, erzeugtes Passwort, Hinweis auf Reverse Proxy und `docs/betrieb/`
|
||||
- [ ] `docs/betrieb/`: `inbetriebnahme.md`, `proxmox.md`, `reverse-proxy.md`, `authentik.md`, `sicherung.md` (`pg_dump` über den Dienstnamen bzw. den Unix-Socket)
|
||||
- [ ] Prüfliste für den Lauf auf echtem Proxmox, da ich den `pct`-Teil hier nicht ausführen kann
|
||||
- [ ] `docs/abnahme-mvp.md` abschließend durchgehen, Review-Agent gegen alle Kriterien, Befunde als Issues
|
||||
- [ ] Devlog-Phasenzusammenfassung: Gesamtstunden, Stunden je Meilenstein, größte Zeitfresser, Abweichung vom Plan
|
||||
- [x] `deploy/install.sh`: native Installation auf Debian 13 — PostgreSQL 17 mit `listen_addresses = ''` (nur Unix-Socket), Node 24 aus dem NodeSource-Repo, corepack/pnpm, Dienstnutzer `laufzettel` ohne Login, Quellcode nach `/opt/laufzettel`, `pnpm install`, `pnpm build`, `prisma migrate deploy`, `prisma db seed`
|
||||
- [x] Geheimnisse im Zielsystem erzeugen: `APP_SECRET` per `openssl rand -base64 48`, Zufallspasswörter für Datenbank und Admin; Ablage in `/etc/laufzettel/laufzettel.env` mit Modus 0600 und Eigentümer root
|
||||
- [x] `laufzettel.service`: systemd-Unit für den Next-standalone-Server, `User=laufzettel`, `EnvironmentFile=`, `NoNewPrivileges=yes`, `ProtectSystem=strict`, `PrivateTmp=yes`, `Restart=on-failure`
|
||||
- [x] Erneuter Aufruf aktualisiert statt neu anzulegen: `git pull`, `pnpm build`, `migrate deploy`, `systemctl restart`; Geheimnisse und Daten bleiben
|
||||
- [x] `deploy/proxmox/laufzettel.sh`: One-Liner für die Proxmox-Shell — `set -Eeuo pipefail`, `trap` mit Zeilennummer, farbige Ausgabe, Spinner, Prüfung auf PVE 8/9 und root
|
||||
- [x] `whiptail`-Dialog Standard gegen Erweitert; Standard: nächste freie CTID, Hostname `laufzettel`, Debian 13, 2 Kerne, 2 GB RAM, 8 GB Platte, unprivilegiert, DHCP auf `vmbr0`
|
||||
- [x] Debian-Vorlage bei Bedarf per `pveam` holen, `pct create`, starten, `deploy/install.sh` im Container ausführen
|
||||
- [x] `--dry-run`: gibt alle `pct`-Aufrufe nur aus, ohne etwas anzulegen
|
||||
- [x] Abschlussbildschirm: URL, Admin-Kennung, erzeugtes Passwort, Hinweis auf Reverse Proxy und `docs/betrieb/`
|
||||
- [x] `docs/betrieb/`: `inbetriebnahme.md`, `proxmox.md`, `reverse-proxy.md`, `authentik.md`, `sicherung.md` (`pg_dump` über den Dienstnamen bzw. den Unix-Socket)
|
||||
- [x] Prüfliste für den Lauf auf echtem Proxmox, da ich den `pct`-Teil hier nicht ausführen kann
|
||||
- [x] `docs/abnahme-mvp.md` abschließend durchgehen, Review-Agent gegen alle Kriterien, Befunde als Issues
|
||||
- [x] Devlog-Phasenzusammenfassung: Gesamtstunden, Stunden je Meilenstein, größte Zeitfresser, Abweichung vom Plan
|
||||
- [ ] `CHANGELOG.md` für `v0.1.0` mit Datum, Version in `package.json`, Tag `v0.1.0`, Gitea-Release mit Changelog-Auszug, Anleitung als PDF beigelegt
|
||||
|
||||
- [ ] Die Anleitung als PDF (`pnpm docs:pdf`) dem Gitea-Release als Datei beilegen — im Repo liegt sie nicht, sie entsteht aus den Kapiteln und Bildern
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- [ ] `docker run --rm debian:13` mit `deploy/install.sh` installiert vollständig durch: Postgres läuft, Migration und Seed gelaufen, Dienst antwortet auf `/api/gesundheit`
|
||||
- [ ] Im nativen Weg lauscht PostgreSQL auf keinem TCP-Port (`ss -ltn` zeigt 5432 nicht)
|
||||
- [ ] Ein zweiter Lauf von `deploy/install.sh` aktualisiert und löscht keine Daten
|
||||
- [ ] `bash deploy/proxmox/laufzettel.sh --dry-run` gibt einen plausiblen, vollständigen `pct create`-Aufruf aus
|
||||
- [ ] `shellcheck --severity=warning` über alle Skripte ohne Befund
|
||||
- [ ] Im Compose-Weg ist die Datenbank vom Host nicht erreichbar, aus `web` schon
|
||||
- [x] `docker run --rm debian:13` mit `deploy/install.sh` installiert vollständig durch: Postgres läuft, Migration und Seed gelaufen, Dienst antwortet auf `/api/gesundheit`
|
||||
- [x] Im nativen Weg lauscht PostgreSQL auf keinem TCP-Port (`ss -ltn` zeigt 5432 nicht)
|
||||
- [x] Ein zweiter Lauf von `deploy/install.sh` aktualisiert und löscht keine Daten
|
||||
- [x] `bash deploy/proxmox/laufzettel.sh --dry-run` gibt einen plausiblen, vollständigen `pct create`-Aufruf aus
|
||||
- [x] `shellcheck --severity=warning` über alle Skripte ohne Befund
|
||||
- [x] Im Compose-Weg ist die Datenbank vom Host nicht erreichbar, aus `web` schon
|
||||
- [ ] `pnpm pruefe:doku` grün, `v0.1.0` getaggt und als Gitea-Release veröffentlicht
|
||||
- [ ] Alle Kriterien aus `docs/abnahme-mvp.md` abgehakt oder als Folge-Issue erfasst
|
||||
- [x] Alle Kriterien aus `docs/abnahme-mvp.md` abgehakt oder als Folge-Issue erfasst
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# #024 Proxmox-Einzeiler auf einem echten PVE-Host gegenprüfen
|
||||
|
||||
**Status:** offen
|
||||
**Im Tracker:** –
|
||||
**Meilenstein:** v0.2.0
|
||||
**Labels:** phase-2, infra
|
||||
**Geschätzt:** 2 h **Tatsächlich:** –
|
||||
|
||||
## Ziel
|
||||
|
||||
`deploy/proxmox/laufzettel.sh` ist statisch geprüft und im Trockenlauf durchgelaufen, aber **nie auf
|
||||
einem Proxmox-Host ausgeführt** — hier stand keiner zur Verfügung. Der Installationsteil
|
||||
(`deploy/install.sh`) dagegen ist vollständig getestet: in `debian:13` ohne systemd und in einem
|
||||
privilegierten Container mit echtem systemd 257, inklusive Aktualisierungslauf, `ProtectSystem`,
|
||||
Sandbox und `/api/gesundheit`.
|
||||
|
||||
Ungeprüft ist damit genau die Klammer darum: `pct`, `pveam`, `pvesh` und `whiptail`.
|
||||
|
||||
Dieses Issue schließt, wenn der Einzeiler einmal auf einem echten PVE-Host gelaufen ist und die
|
||||
Prüfliste in [docs/betrieb/proxmox.md](../betrieb/proxmox.md) („Was du beim ersten Lauf selbst
|
||||
nachprüfen musst“) abgehakt ist. Die Liste ist der Prüfplan, nicht dieses Issue.
|
||||
|
||||
## Konkret ungeprüft
|
||||
|
||||
- [ ] `pveversion` erkennt PVE 8 und 9 richtig; Abbruch auf einem Nicht-PVE-Rechner
|
||||
- [ ] `pveam available` / `pveam download` finden die Debian-13-Standardvorlage
|
||||
- [ ] `pct create` nimmt die zusammengebauten Argumente an (Speicher mit Inhalt `rootdir`,
|
||||
unprivilegiert, DHCP auf `vmbr0`)
|
||||
- [ ] Die Ermittlung der nächsten freien CTID über `pvesh` stimmt mit dem, was PVE vergibt
|
||||
- [ ] Der Container bekommt eine Adresse, und `CT_IP` wird richtig abgelesen — davon hängen `APP_URL`
|
||||
und der Abschlussbildschirm ab
|
||||
- [ ] `pct exec … bash -c "<Umgebung> bash /root/laufzettel-install.sh"` übergibt die Variablen so,
|
||||
wie `install.sh` sie liest (`APP_URL`, `BIND_ADDR`, `LAUFZETTEL_REPO`, `LAUFZETTEL_ZWEIG`,
|
||||
`LAUFZETTEL_PASSWORT_DATEI`)
|
||||
- [ ] Das Erstpasswort wird aus `/root/laufzettel-erstpasswort` gelesen **und danach gelöscht**
|
||||
- [ ] Der `whiptail`-Dialog ist auf einer PVE-Shell bedienbar (Standard und Erweitert)
|
||||
- [ ] Der zweite Aufruf erkennt den Container am Hostnamen und aktualisiert statt anzulegen
|
||||
- [ ] `--aktualisieren` bricht ab, wenn es keinen Container `laufzettel` gibt
|
||||
- [ ] Die Ausgabe von `install.sh` erscheint im Terminal, ohne von einem Spinner überschrieben zu
|
||||
werden
|
||||
|
||||
## Aufgaben
|
||||
|
||||
- [ ] Einzeiler auf einem PVE-Host fahren, Prüfliste abhaken
|
||||
- [ ] Abweichungen als eigene Issues aufnehmen, nicht hier sammeln
|
||||
- [ ] Nach dem erfolgreichen Lauf den Satz „Der `pct`-Teil ist ungetestet“ in
|
||||
`docs/betrieb/proxmox.md` durch das Ergebnis ersetzen (PVE-Fassung, Datum)
|
||||
- [ ] `docs/abnahme-mvp.md` Zeile 11 abhaken
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- [ ] Ein LXC, in dem `systemctl is-active laufzettel` „active“ meldet und `/api/gesundheit`
|
||||
`status ok` liefert
|
||||
- [ ] `pct exec <ctid> -- ss -ltn` zeigt keinen Port 5432
|
||||
- [ ] Zweiter Aufruf: Daten, `APP_SECRET` und Datenbankpasswort unverändert
|
||||
@@ -0,0 +1,35 @@
|
||||
# #025 Healthcheck gibt die rohe Datenbankfehlermeldung heraus
|
||||
|
||||
**Status:** offen
|
||||
**Im Tracker:** –
|
||||
**Meilenstein:** v0.2.0
|
||||
**Labels:** phase-2, backend
|
||||
**Geschätzt:** 1 h **Tatsächlich:** –
|
||||
|
||||
## Ziel
|
||||
|
||||
`src/app/api/gesundheit/route.ts` antwortet im Fehlerfall mit
|
||||
|
||||
```json
|
||||
{ "status": "fehler", "meldung": "<fehler.message>" }
|
||||
```
|
||||
|
||||
Der Pfad ist absichtlich ohne Anmeldung erreichbar — er steht in der Allowlist des Proxys, und
|
||||
`tests/sicherheitsrichtlinie.test.ts` hält das fest. Eine Fehlermeldung von Prisma oder `pg` nennt
|
||||
typischerweise Host, Port, Benutzername und Datenbanknamen. Das ist für einen öffentlich erreichbaren
|
||||
Endpunkt zu viel: er soll sagen, **ob** es geht, nicht warum nicht.
|
||||
|
||||
Aufgefallen beim Abnahme-Review zu [#019](019-betrieb-release.md).
|
||||
|
||||
## Aufgaben
|
||||
|
||||
- [ ] Nach außen nur `{ "status": "fehler" }` mit Status 503
|
||||
- [ ] Die Begründung serverseitig protokollieren, damit sie beim Suchen nicht fehlt
|
||||
- [ ] Prüfen, ob `docs/betrieb/` irgendwo die Meldung als Diagnosehilfe nennt — dann auf das Log
|
||||
verweisen
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- [ ] Test: bei nicht erreichbarer Datenbank enthält die Antwort weder Host noch Port noch
|
||||
Benutzernamen
|
||||
- [ ] Test: der Statuscode bleibt 503, damit Docker und der Reverse Proxy weiter richtig entscheiden
|
||||
@@ -0,0 +1,37 @@
|
||||
# #026 Zwei Konventionen präzisieren, die strenger klingen als sie gemeint sind
|
||||
|
||||
**Status:** offen
|
||||
**Im Tracker:** –
|
||||
**Meilenstein:** v0.2.0
|
||||
**Labels:** phase-2, docs
|
||||
**Geschätzt:** 1 h **Tatsächlich:** –
|
||||
|
||||
## Ziel
|
||||
|
||||
Zwei Sätze in `CLAUDE.md` (und gleichlautend in `docs/architektur.md`) beschreiben den Code nicht
|
||||
genau. Beide fielen beim Abnahme-Review zu [#019](019-betrieb-release.md) auf.
|
||||
|
||||
**1. Rohe Hexwerte.** Die Regel sagt „Komponenten greifen nur darauf zu, nie auf rohe Hexwerte“.
|
||||
Tatsächlich stehen vier begründete Ausnahmen im Code: `#ffffff` in `drucken/lauf/[id]/page.tsx` und
|
||||
`drucken/etiketten/page.tsx` (Druck ist immer hell, unabhängig vom Farbschema der Anwendung) sowie
|
||||
`#0B0D10` in `layout.tsx` und `manifest.ts` (`themeColor` und das Manifest vertragen keine
|
||||
CSS-Variablen). Die Regel ist strenger formuliert als gemeint — sie soll um diese Ausnahmen ergänzt
|
||||
werden, nicht der Code geändert.
|
||||
|
||||
**2. `pnpm test` ohne LDAP-Container.** „Vor jedem Commit“ in `CLAUDE.md` und „Qualitätsschranken“ im
|
||||
README nennen `pnpm test`, aber nicht
|
||||
`docker compose -f docker-compose.test.yml up -d ldap`. Ohne den Container überspringt die Suite die
|
||||
Tests gegen das echte Verzeichnis — sie sagt das auf stderr, aber der Lauf sieht grün aus. Wer nur
|
||||
dem dokumentierten Weg folgt, prüft den Verzeichnis-Abgleich nie. Ein Satz in beiden Dateien.
|
||||
|
||||
## Aufgaben
|
||||
|
||||
- [ ] Ausnahme für rohe Hexwerte in `CLAUDE.md` und `docs/architektur.md` benennen, mit Begründung
|
||||
- [ ] Den LDAP-Container in `CLAUDE.md` („Vor jedem Commit“) und `README.md` aufnehmen
|
||||
- [ ] Prüfen, ob sich der Hinweis maschinell absichern lässt — `pruefe:doku` könnte verlangen, dass
|
||||
jede in `CLAUDE.md` genannte Schranke auch den nötigen Vorlauf nennt
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- [ ] Die vier Hexwerte im Code stehen in Einklang mit der Regel, ohne den Code zu ändern
|
||||
- [ ] Wer `CLAUDE.md` von oben nach unten befolgt, fährt die Suite mit LDAP-Container
|
||||
@@ -23,9 +23,12 @@ Diese Dateien sind die Quelle; `pnpm issues:sync` spiegelt sie in den Gitea-Trac
|
||||
| #016 | [M11 – Verzeichnis und LDAP-Abgleich](016-verzeichnis-ldap.md) | erledigt | v0.1.0 | 12 h | 3 h 25 min |
|
||||
| #017 | [M12 – Seed der sieben Vorlagen](017-seed-sieben-vorlagen.md) | erledigt | v0.1.0 | 8 h | 1 h 30 min |
|
||||
| #018 | [M13 – E2E-Tests, Anleitung, PDF](018-e2e-anleitung-pdf.md) | erledigt | v0.1.0 | 14 h | 2 h 15 min |
|
||||
| #019 | [M14 – Betriebswege und Release v0.1.0](019-betrieb-release.md) | offen | v0.1.0 | 10 h | – |
|
||||
| #019 | [M14 – Betriebswege und Release v0.1.0](019-betrieb-release.md) | erledigt | v0.1.0 | 10 h | 5 h 25 min |
|
||||
| #020 | [Vorgesetzter auch für von Hand angelegte Personen](020-vorgesetzter-manuell.md) | offen | v0.2.0 | 3 h | – |
|
||||
| #021 | [Stabiler Schlüssel für Seed-Vorlagen](021-stabiler-schluessel-vorlagen.md) | offen | v0.2.0 | 2 h | – |
|
||||
| #022 | [Schnellstart je Gerätetyp pflegen](022-schnellstart-pflegen.md) | offen | v0.2.0 | 3 h | – |
|
||||
| #023 | [Protokoll ansehen](023-protokoll-ansicht.md) | offen | v0.2.0 | 4 h | – |
|
||||
| | **Summe** | | | **148 h** | **29 h 55 min** |
|
||||
| #024 | [Proxmox-Einzeiler auf einem echten PVE-Host gegenprüfen](024-proxmox-echtlauf.md) | offen | v0.2.0 | 2 h | – |
|
||||
| #025 | [Healthcheck gibt die rohe Datenbankfehlermeldung heraus](025-gesundheit-fehlermeldung.md) | offen | v0.2.0 | 1 h | – |
|
||||
| #026 | [Zwei Konventionen präzisieren, die strenger klingen als sie gemeint sind](026-konventionen-praezisieren.md) | offen | v0.2.0 | 1 h | – |
|
||||
| | **Summe** | | | **152 h** | **35 h 20 min** |
|
||||
|
||||
Reference in New Issue
Block a user