diff --git a/CLAUDE.md b/CLAUDE.md index dde85d8..c1ec571 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -96,6 +96,9 @@ Statistiken, Benachrichtigungen, REST-API, PWA. Oberflächensprache **Deutsch**. - **Dev-Umgebung**: Postgres auf `localhost:5439` (`docker-compose.dev.yml`), Dev-Server auf **Port 3011** (3000 und 3010 sind von anderen Projekten belegt). - **Screenshots**: `node scripts/shot.mjs [w] [h] [theme]`. + Achtung: das Skript meldet sich **nicht** an — bei geschützten Seiten entsteht + kommentarlos ein Bild der Anmeldemaske. Für angemeldete Ansichten den + Login-Weg aus `scripts/screenshots.mjs` nachbauen. - Next hängt selbst einen Block an diese Datei an — der bleibt drin. ## Fortschritt @@ -119,6 +122,18 @@ Statistiken, Benachrichtigungen, REST-API, PWA. Oberflächensprache **Deutsch**. das ASCII-Schlusszeichen beendet den String. In JSX-Text und Kommentaren ok. - Heredocs im Bash-Tool sind bei langen TSX-Dateien unzuverlässig — dann das Write-Werkzeug benutzen. +- **Klickwege niemals gegen `npm run dev` prüfen.** Unter headless Chromium + scheitert der HMR-Socket (`ERR_INVALID_HTTP_RESPONSE`), dadurch hydratisiert + **keine** Seite und kein Knopf reagiert — ohne Seitenfehler, ohne + fehlgeschlagene Anfrage, es passiert einfach nichts. Sieht aus wie ein Fehler + im eigenen Code, ist aber die Umgebung. Stattdessen + `npm run build` und `PORT=3012 npm start`, dann dagegen klicken. + Formulare täuschen zusätzlich: die Anmeldung funktioniert auch ohne + JavaScript, wirkt also intakt. +- Vor `npm start` prüfen, ob der Port noch von einem früheren Lauf belegt ist — + sonst startet der neue Server gar nicht und man testet den alten Bau. +- Testdaten nach Klickproben wieder herstellen: die Proben schreiben in die + geseedete Entwicklungsdatenbank. diff --git a/update.sh b/update.sh index 5bc2eb4..5a48fbe 100755 --- a/update.sh +++ b/update.sh @@ -39,6 +39,10 @@ Ablauf: Datenbank sichern → Änderungen holen → Image bauen → neu starten warten, bis die Instanz wieder antwortet. Die Datenbankmigrationen spielt der Container beim Start selbst ein. +Geprüft wird zuerst http://127.0.0.1:APP_PORT/api/health. Ist der Port gar +nicht auf dem Host veröffentlicht — üblich hinter einem Reverse Proxy —, +zählt stattdessen der Healthcheck des Containers. + Optionen: --ohne-backup Keine Sicherung anlegen (nicht empfohlen) --ohne-pull Nichts aus dem Repository holen, nur neu bauen und starten @@ -196,31 +200,95 @@ gut "Container laufen." schritt "Auf die Instanz warten" info "Die Datenbankmigrationen spielt der Container beim Start selbst ein." +# Der Container meldet seinen Zustand selbst — der HEALTHCHECK im Image fragt +# /api/health von innen ab. Das ist der verlässlichere Weg: hinter einem +# Reverse Proxy ist der App-Port oft gar nicht auf dem Host veröffentlicht, +# und dann sagt ein fehlschlagendes curl nichts über die Instanz aus. +# Gibt immer 0 zurück: die Funktion läuft auch im Fehlerpfad, und unter +# `set -e` dürfte eine Auskunft über den Zustand nicht selbst das Skript +# abbrechen, bevor der Weg zurück ausgegeben wurde. +container_zustand() { + local id + id=$($COMPOSE ps -q app 2>/dev/null || true) + if [ -z "$id" ]; then + printf 'kein-container' + return 0 + fi + # Ohne HEALTHCHECK im Image ist .State.Health leer — dann bleibt der + # Laufzustand (running/exited) als grobe Auskunft. + docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}ohne-healthcheck:{{.State.Status}}{{end}}' \ + "$id" 2>/dev/null || printf 'unbekannt' + return 0 +} + GESUND=0 +UEBER_CONTAINER=0 for _ in $(seq 1 60); do if curl -fsS "http://127.0.0.1:${PORT}/api/health" >/dev/null 2>&1; then GESUND=1 break fi + if [ "$(container_zustand)" = "healthy" ]; then + GESUND=1 + UEBER_CONTAINER=1 + break + fi sleep 2 done if [ "$GESUND" -eq 1 ]; then - ANTWORT=$(curl -fsS "http://127.0.0.1:${PORT}/api/health" 2>/dev/null || true) - gut "Flimmer antwortet auf Port ${PORT}." - leise "$ANTWORT" + if [ "$UEBER_CONTAINER" -eq 1 ]; then + gut "Der Container meldet sich gesund." + leise "Auf Port ${PORT} war nichts zu erreichen — das ist normal, wenn die App" + leise "nur über einen Reverse Proxy erreichbar ist und den Port nicht veröffentlicht." + else + ANTWORT=$(curl -fsS "http://127.0.0.1:${PORT}/api/health" 2>/dev/null || true) + gut "Flimmer antwortet auf Port ${PORT}." + leise "$ANTWORT" + fi printf '\n%s✓ Fertig — Version %s%s\n' "$GUT" "$NEUE_VERSION" "$AUS" if [ "$VORHER_VERSION" != "$NEUE_VERSION" ]; then info "Was sich geändert hat, steht in CHANGELOG.md." fi - [ -n "$SICHERUNG" ] && leise "Sicherung: $SICHERUNG" + if [ -n "$SICHERUNG" ]; then + leise "Sicherung: $SICHERUNG" + fi exit 0 fi # --- Fehlschlag: konkreter Weg zurück ---------------------------------------- printf '\n%s✗ Die Instanz antwortet nach zwei Minuten nicht.%s\n\n' "$FEHLER" "$AUS" >&2 + +# Der Container-Zustand grenzt ein, wo es klemmt — ohne ihn sähe ein nicht +# veröffentlichter Port genauso aus wie eine abgestürzte App. +ZUSTAND=$(container_zustand) +case "$ZUSTAND" in + kein-container) + info "Es läuft gar kein app-Container. Der Start ist offenbar fehlgeschlagen." + ;; + starting) + info "Der Container startet noch. Möglicherweise dauern die Migrationen länger —" + info "sieh im Log nach und gib ihm etwas Zeit." + ;; + unhealthy) + info "Der Container läuft, meldet sich aber als nicht gesund. Der Fehler liegt" + info "also in der Anwendung, nicht am Port." + ;; + ohne-healthcheck:running) + info "Der Container läuft, das Image bringt aber keinen Healthcheck mit (ältere" + info "Fassung). Ob die App wirklich antwortet, verrät nur das Log." + ;; + ohne-healthcheck:*) + info "Der Container ist nicht gelaufen: ${ZUSTAND#ohne-healthcheck:}" + ;; + *) + info "Container-Zustand: ${ZUSTAND}" + ;; +esac +printf '\n' + info "Log ansehen:" printf ' %s logs --tail 50 app\n\n' "$COMPOSE"