diff --git a/.env.example b/.env.example index ac2f645..6b80b28 100644 --- a/.env.example +++ b/.env.example @@ -47,6 +47,10 @@ AUTH_TRUST_HOST=true APP_BIND=127.0.0.1 APP_PORT=3000 +# Die Konsole des mitgelieferten Objektspeichers. Nur noetig, wenn auf dem +# Server schon etwas auf 9001 laeuft. +MINIO_KONSOLE_PORT=9001 + # --- Anwendungsseitige Feldverschluesselung --------------------------------- # AES-256-GCM. 32 Bytes, base64. OHNE DIESEN SCHLUESSEL SIND DIE VERSCHLUESSELTEN # FELDER UNWIEDERBRINGLICH VERLOREN - unbedingt getrennt vom Backup sichern! diff --git a/CHANGELOG.md b/CHANGELOG.md index b85a95d..85b2995 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,47 @@ Wiederherstellung*. _Noch nichts._ +## [1.0.5] – 2026-08-22 + +### Hinzugefügt + +- **`scripts/update.sh`** — aktualisiert die Anwendung mit einem Befehl. + + ```bash + ./scripts/update.sh # auf die neueste Fassung + ./scripts/update.sh --pruefen # nur nachsehen, nichts ändern + ./scripts/update.sh v1.2.0 # auf eine bestimmte Fassung + ``` + + Es nimmt die Schritte ab, die man von Hand vergisst: Sicherung + anstoßen und bei Misserfolg **abbrechen**, den Changelog zwischen altem + und neuem Stand anzeigen, umschalten, neu bauen, warten bis die + Anwendung antwortet, **die beiden SQL-Skripte ausführen** und zum + Schluss prüfen, ob wirklich die neue Fassung läuft. + + Zwei Eigenschaften, die dabei zählen: + + **Bis zur Rückfrage wird nichts verändert.** Bricht etwas vorher ab — + Server nicht erreichbar, falsche Versionsangabe — findest du alles so + vor wie zuvor. Kein Halbzustand. + + **Eigene Änderungen an versionierten Dateien blockieren nicht.** Im + Betrieb passt man die `docker-compose.yml` an, bis etwas läuft; genau + diese Anpassungen sind später oft schon eingearbeitet. Das Skript legt + sie vor dem Umschalten mit `git stash` beiseite und sagt, wie man sie + zurückholt. Mit `--verwerfen` werden sie stattdessen weggeworfen. Die + `.env` bleibt immer unangetastet — sie ist nicht versioniert. + + Schlägt der Bau fehl, nennt das Skript den Weg zurück auf den + vorherigen Stand. + +### Behoben + +- **Der Port der MinIO-Konsole ließ sich nicht ändern.** Er stand fest auf + `9001`; lief dort schon etwas, startete der ganze Stapel nicht + (`port is already allocated`). Jetzt über `MINIO_KONSOLE_PORT` in der + `.env` einstellbar — dieselbe Möglichkeit, die `APP_PORT` schon hatte. + ## [1.0.4] – 2026-08-22 ### Behoben @@ -351,7 +392,8 @@ Was davon wann angegangen wird, steht in [`docs/ROADMAP.md`](docs/ROADMAP.md). --- -[Unveröffentlicht]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.4...HEAD +[Unveröffentlicht]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.5...HEAD +[1.0.5]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.4...v1.0.5 [1.0.4]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.3...v1.0.4 [1.0.3]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.2...v1.0.3 [1.0.2]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.1...v1.0.2 diff --git a/README.md b/README.md index 28c0fca..2006630 100644 --- a/README.md +++ b/README.md @@ -177,12 +177,18 @@ existiert. ### Aktualisieren ```bash -git pull -docker compose up -d --build +./scripts/update.sh ``` -Die Migrationen laufen beim Start des Anwendungsbehälters mit. **Vorher ein -Backup anstoßen** (siehe unten) — eine Migration lässt sich nicht zurückrollen. +Das Skript sichert zuerst, zeigt den Changelog zwischen altem und neuem +Stand, fragt nach, schaltet um, baut neu und führt danach die beiden +SQL-Skripte aus. Schlägt die Sicherung fehl, wird nicht aktualisiert. + +`--pruefen` sieht nur nach, ohne etwas zu ändern. Eigene Änderungen an +versionierten Dateien werden vor dem Umschalten beiseite gelegt +(`git stash`), die `.env` bleibt unangetastet. + +Einzelheiten und der Weg von Hand: [`docs/INSTALLATION.md`](docs/INSTALLATION.md). --- diff --git a/docker-compose.yml b/docker-compose.yml index bbc61be..522f029 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -66,7 +66,10 @@ services: ports: # Nur an die Loopback-Adresse gebunden. Die Konsole erreichst du # ueber einen SSH-Tunnel oder ueber deinen vorhandenen Proxy. - - '127.0.0.1:9001:9001' + # + # Laeuft auf dem Server schon etwas auf 9001, weich in der .env aus: + # MINIO_KONSOLE_PORT=9011 + - '${MINIO_KONSOLE_BIND:-127.0.0.1}:${MINIO_KONSOLE_PORT:-9001}:9001' networks: [intern] app: @@ -93,7 +96,7 @@ services: ENC_KEY_V1: ${ENC_KEY_V1:?ENC_KEY_V1 fehlt in der .env - ohne ihn sind die verschluesselten Felder nicht lesbar} # Bleibt leer, solange nicht rotiert wird. Die Anwendung ueberspringt # leere Schluesselvariablen - siehe src/lib/env.ts. - ENC_KEY_V2: ${ENC_KEY_V2:-} + # ENC_KEY_V2: ${ENC_KEY_V2:-} ENC_KEY_AKTIV: ${ENC_KEY_AKTIV:-1} INDEX_KEY: ${INDEX_KEY:?INDEX_KEY fehlt in der .env} SESSION_MAX_AGE: ${SESSION_MAX_AGE:-1800} diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 785eed7..227985f 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -832,27 +832,80 @@ Denk an die letzten Pflichten: ## Aktualisieren +```bash +cd /opt/betreuungs-cockpit +./scripts/update.sh +``` + +Das war es. Das Skript nimmt dir die Schritte ab, die man von Hand +vergisst: + +1. Prüft Verzeichnis, git, docker und `.env` +2. Holt die Fassungen und zeigt, was zwischen deinem Stand und dem neuen + im Changelog steht +3. Fragt nach +4. **Stößt eine Sicherung an** — und bricht ab, wenn sie nicht klappt +5. Legt eigene Änderungen an versionierten Dateien beiseite, schaltet um, + baut neu +6. Wartet, bis die Anwendung antwortet +7. Führt die **beiden SQL-Skripte** aus (Volltextsuche, Audit-Schutz) +8. Prüft, ob wirklich die neue Fassung läuft + +**Bis Schritt 3 wird nichts verändert.** Bricht etwas vorher ab, findest du +alles so vor wie zuvor. + +### Aufrufmöglichkeiten + +| Aufruf | Wirkung | +|---|---| +| `./scripts/update.sh` | auf die neueste veröffentlichte Fassung | +| `./scripts/update.sh v1.2.0` | auf eine bestimmte Fassung | +| `./scripts/update.sh --pruefen` | nur nachsehen, nichts ändern | +| `./scripts/update.sh --ja` | ohne Rückfrage (für cron) | +| `./scripts/update.sh --verwerfen` | eigene Änderungen wegwerfen statt beiseitelegen | +| `./scripts/update.sh --ohne-sicherung` | Sicherung überspringen — sehr gut überlegen | + +### Eigene Änderungen an der docker-compose.yml + +Das ist der Normalfall: Man passt etwas an, bis es läuft. Das Skript legt +solche Änderungen vor dem Umschalten mit `git stash` beiseite und sagt, +wie man sie zurückholt: + +```bash +git stash list # was liegt da +git stash pop # zurückholen +git stash drop # wegwerfen +``` + +Sind deine Anpassungen inzwischen im Projekt eingearbeitet — wie +`APP_BIND` oder `MINIO_KONSOLE_PORT` —, brauchst du sie nicht +zurückzuholen. Dann setz die Werte einfach in der `.env`. + +> **Die `.env` wird nie angefasst.** Sie ist nicht versioniert; weder +> `git checkout` noch das Skript rühren sie an. + +### Von Hand, wenn du lieber selbst steuerst + ```bash cd /opt/betreuungs-cockpit # 1. IMMER zuerst sichern - eine Migration lässt sich nicht zurückrollen docker compose exec sicherung /bin/sh /sicherung.sh -# 2. Was ändert sich? Lies den Changelog, bevor du ziehst +# 2. Was ändert sich? git fetch --tags git log --oneline HEAD..origin/main -# 3. Auf die neue Fassung wechseln +# 3. Umschalten git checkout v1.1.0 # 4. Neu bauen und starten -GIT_COMMIT=$(git rev-parse --short HEAD) \ -BUILD_ZEIT=$(date -Iseconds) \ -docker compose up -d --build -``` +GIT_COMMIT=$(git rev-parse --short HEAD) BUILD_ZEIT=$(date -Iseconds) docker compose up -d --build -Die Migrationen laufen beim Start mit. **Sagt der Changelog, dass sich ein -SQL-Skript geändert hat, führ die beiden Befehle aus Schritt 5 erneut aus.** +# 5. Die beiden SQL-Skripte - sie laufen NICHT mit den Migrationen mit +docker compose exec app node node_modules/prisma/build/index.js db execute --schema prisma/schema.prisma --file prisma/sql/volltextsuche.sql +docker compose exec app node node_modules/prisma/build/index.js db execute --schema prisma/schema.prisma --file prisma/sql/audit-schutz.sql +``` ### Kontrolle @@ -862,7 +915,21 @@ curl -s https://cockpit.deine-domain.de/api/gesundheit ``` Steht dort noch die alte Version, läuft der alte Behälter weiter — dann -hat der Bau nicht durchgezogen. Sieh in `docker compose logs app`. +hat der Bau nicht durchgezogen: + +```bash +docker compose up -d --build --force-recreate app +``` + +### Zurück auf den vorherigen Stand + +```bash +git checkout v1.0.4 +docker compose up -d --build +``` + +Hat die neue Fassung die Datenbank migriert, reicht das nicht — dann muss +auch die Sicherung zurück (siehe Schritt 9). --- diff --git a/package.json b/package.json index cba56d9..923379d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "betreuungs-cockpit", - "version": "1.0.4", + "version": "1.0.5", "private": true, "description": "Arbeits- und Dokumentationswerkzeug fuer die ehrenamtliche rechtliche Betreuung", "engines": { diff --git a/scripts/update.sh b/scripts/update.sh new file mode 100644 index 0000000..1b98331 --- /dev/null +++ b/scripts/update.sh @@ -0,0 +1,356 @@ +#!/usr/bin/env bash +# +# Betreuungs-Cockpit aktualisieren. +# +# ./scripts/update.sh auf die neueste veroeffentlichte Fassung +# ./scripts/update.sh v1.2.0 auf eine bestimmte Fassung +# ./scripts/update.sh --ja ohne Rueckfrage (fuer cron) +# ./scripts/update.sh --pruefen nur nachsehen, nichts aendern +# ./scripts/update.sh --verwerfen eigene Aenderungen an versionierten +# Dateien wegwerfen statt beiseitelegen +# +# Was das Skript tut, in dieser Reihenfolge: +# +# 1. Voraussetzungen pruefen (Verzeichnis, git, docker, .env) +# 2. Fassungen holen und vergleichen +# 3. Zeigen, was sich aendert (Changelog-Abschnitte dazwischen) +# 4. Nachfragen +# 5. SICHERUNG anstossen - und abbrechen, wenn sie nicht klappt +# 6. Eigene Aenderungen beiseitelegen, umschalten, neu bauen, starten +# 7. Die beiden SQL-Skripte ausfuehren (sie sind idempotent) +# 8. Pruefen, ob die neue Fassung wirklich laeuft +# +# Die Reihenfolge ist Absicht: Bis einschliesslich Schritt 4 wird nichts +# veraendert. Wer vorher abbricht - oder wobei etwas schiefgeht -, findet +# alles so vor wie zuvor. Erst danach wird angefasst. +# +# Schritt 5 ist der wichtigste: Eine Datenbank-Migration laesst sich nicht +# zurueckrollen. Ohne Sicherung wird hier nichts aktualisiert. +# +# Schritt 7 ebenfalls: Die SQL-Skripte laufen NICHT mit den Migrationen +# mit. Wer sie vergisst, hat einen veralteten Trigger und merkt es erst, +# wenn etwas nicht geht. +# +# Die .env wird nie angefasst - sie ist nicht versioniert. + +# Die geschweiften Klammern sorgen dafuer, dass bash die ganze Datei liest, +# bevor sie irgendetwas ausfuehrt. Ohne sie koennte der git-checkout weiter +# unten das Skript unter den eigenen Fuessen austauschen. +{ +set -euo pipefail + +# --- Ausgabe ----------------------------------------------------------------- + +if [ -t 1 ]; then + ROT=$'\033[31m'; GRUEN=$'\033[32m'; GELB=$'\033[33m'; FETT=$'\033[1m'; AUS=$'\033[0m' +else + ROT=''; GRUEN=''; GELB=''; FETT=''; AUS='' +fi + +schritt() { printf '\n%s==> %s%s\n' "$FETT" "$1" "$AUS"; } +gut() { printf ' %s%s%s\n' "$GRUEN" "$1" "$AUS"; } +warnung() { printf ' %s%s%s\n' "$GELB" "$1" "$AUS"; } +abbruch() { printf '\n%sAbbruch: %s%s\n\n' "$ROT" "$1" "$AUS" >&2; exit 1; } + +# --- Aufrufparameter --------------------------------------------------------- + +ZIEL="" +OHNE_RUECKFRAGE=0 +NUR_PRUEFEN=0 +OHNE_SICHERUNG=0 +VERWERFEN=0 +ENTSCHEIDUNG="" + +for arg in "$@"; do + case "$arg" in + --ja|-j) OHNE_RUECKFRAGE=1 ;; + --pruefen|-p) NUR_PRUEFEN=1 ;; + --ohne-sicherung) OHNE_SICHERUNG=1 ;; + --verwerfen) VERWERFEN=1 ;; + --hilfe|-h) + sed -n '2,34p' "$0" | sed 's/^#\s\?//' + exit 0 ;; + -*) abbruch "Unbekannte Angabe: $arg (--hilfe zeigt die Moeglichkeiten)" ;; + *) ZIEL="$arg" ;; + esac +done + +# --- 1. Voraussetzungen ------------------------------------------------------ + +schritt "Voraussetzungen" + +cd "$(dirname "$0")/.." || abbruch "Das Projektverzeichnis ist nicht erreichbar." +PROJEKT="$(pwd)" + +[ -f docker-compose.yml ] || abbruch "Hier liegt keine docker-compose.yml: $PROJEKT" +[ -f .env ] || abbruch "Es gibt keine .env. Ohne sie startet die Anwendung nicht." +command -v git >/dev/null || abbruch "git ist nicht installiert." +docker compose version >/dev/null 2>&1 || abbruch "docker compose (v2) ist nicht verfuegbar." + +gut "Verzeichnis: $PROJEKT" + +# Nur nachsehen - angefasst wird die Arbeitskopie erst kurz vor dem +# Umschalten, wenn Sicherung und Rueckfrage durch sind. Ein Abbruch +# dazwischen darf keinen Halbzustand hinterlassen. +SCHMUTZIG="" +if [ -n "$(git status --porcelain --untracked-files=no)" ]; then + SCHMUTZIG="ja" + warnung "Es gibt eigene Aenderungen an versionierten Dateien." + git status --short --untracked-files=no | sed 's/^/ /' + printf ' Sie werden vor dem Umschalten beiseite gelegt.\n' +else + gut "Arbeitskopie ist sauber" +fi + +# --- 2. Was ist da, was kommt ------------------------------------------------ + +schritt "Fassungen" + +if ! git fetch --tags --quiet origin; then + abbruch "Konnte die Fassungen nicht vom Server holen. + Erreichst du ihn? Stimmen die Zugangsdaten? + Es wurde nichts geaendert." +fi + +JETZT="$(git describe --tags --exact-match 2>/dev/null || git rev-parse --short HEAD)" +NEUESTE="$(git tag --list 'v*' --sort=-v:refname | head -1)" + +[ -n "$NEUESTE" ] || abbruch "Es gibt keine veroeffentlichte Fassung (kein Tag)." + +if [ -z "$ZIEL" ]; then + ZIEL="$NEUESTE" +fi + +git rev-parse --verify --quiet "$ZIEL^{commit}" >/dev/null \ + || abbruch "Die Fassung '$ZIEL' gibt es nicht. Vorhanden: +$(git tag --list 'v*' --sort=-v:refname | head -5 | sed 's/^/ /')" + +printf ' Jetzt: %s\n' "$JETZT" +printf ' Ziel: %s%s%s\n' "$FETT" "$ZIEL" "$AUS" + +if [ "$(git rev-parse HEAD)" = "$(git rev-parse "$ZIEL^{commit}")" ]; then + gut "Bereits auf dem gewuenschten Stand - nichts zu tun." + exit 0 +fi + +# --- 3. Was aendert sich ----------------------------------------------------- + +schritt "Was sich aendert" + +if [ -f CHANGELOG.md ]; then + # Alle Abschnitte zwischen dem laufenden und dem gewuenschten Stand. + ZIEL_NR="${ZIEL#v}" + JETZT_NR="${JETZT#v}" + awk -v ziel="$ZIEL_NR" -v jetzt="$JETZT_NR" ' + /^## \[/ { + nummer = $0 + sub(/^## \[/, "", nummer); sub(/\].*/, "", nummer) + drucken = (nummer != "Unveröffentlicht" && nummer != jetzt) + if (nummer == jetzt) exit + } + drucken { print " " $0 } + ' CHANGELOG.md | head -60 +else + warnung "Kein CHANGELOG.md gefunden." +fi + +printf '\n' +git log --oneline --no-decorate "HEAD..$ZIEL" | head -15 | sed 's/^/ /' + +if [ "$NUR_PRUEFEN" = "1" ]; then + printf '\n' + gut "Nur geprueft - es wurde nichts geaendert." + [ -n "$SCHMUTZIG" ] && warnung "Beim echten Lauf wuerden deine Aenderungen beiseite gelegt." + exit 0 +fi + +# --- 4. Nachfragen ----------------------------------------------------------- + +if [ "$OHNE_RUECKFRAGE" != "1" ]; then + printf '\n%sJetzt aktualisieren auf %s? [j/N] %s' "$FETT" "$ZIEL" "$AUS" + read -r antwort /dev/null | grep -qx sicherung; then + if docker compose exec -T sicherung /bin/sh /sicherung.sh; then + gut "Sicherung angelegt und selbst geprueft." + else + abbruch "Die Sicherung ist fehlgeschlagen. Es wird nichts aktualisiert. + Mit --ohne-sicherung laesst sich das uebergehen - aber ueberleg + dir das zweimal." + fi + else + warnung "Der Dienst 'sicherung' laeuft nicht - es wurde nichts gesichert." + if [ "$OHNE_RUECKFRAGE" != "1" ]; then + printf '\n %sTrotzdem weitermachen? [j/N] %s' "$FETT" "$AUS" + read -r weiter /dev/null || true)" + case "$ANTWORT" in *'"bereit"'*) break ;; esac + printf '.' + sleep 3 +done +printf '\n' + +case "$ANTWORT" in + *'"bereit"'*) gut "Die Anwendung antwortet." ;; + *) + printf '\n' + docker compose logs app --tail 40 | sed 's/^/ /' + abbruch "Die Anwendung antwortet nicht unter $GESUND. + Die letzten Zeilen aus dem Log stehen oben. + Zurueck geht es mit: git checkout $JETZT && docker compose up -d --build" + ;; +esac + +# --- 8. SQL-Skripte ---------------------------------------------------------- + +schritt "Volltextsuche und Audit-Schutz" +printf ' Diese beiden laufen nicht mit den Migrationen mit.\n' +printf ' Sie sind idempotent - mehrfaches Ausfuehren schadet nicht.\n\n' + +PRISMA="node node_modules/prisma/build/index.js" + +for skript in volltextsuche audit-schutz; do + if docker compose exec -T app $PRISMA db execute \ + --schema prisma/schema.prisma --file "prisma/sql/${skript}.sql" >/dev/null 2>&1; then + gut "${skript}.sql ausgefuehrt" + else + warnung "${skript}.sql ist fehlgeschlagen - bitte von Hand nachholen:" + printf ' docker compose exec app %s db execute \\\n' "$PRISMA" + printf ' --schema prisma/schema.prisma --file prisma/sql/%s.sql\n' "$skript" + fi +done + +# --- 9. Nachschau ------------------------------------------------------------ + +schritt "Ergebnis" + +LAEUFT="$(printf '%s' "$ANTWORT" | sed -n 's/.*"version":"\([^"]*\)".*/\1/p')" +ERWARTET="${ZIEL#v}" + +printf ' Erwartet: %s\n' "$ERWARTET" +printf ' Laeuft: %s\n' "${LAEUFT:-unbekannt}" + +if [ "$LAEUFT" = "$ERWARTET" ]; then + printf '\n' + gut "Fertig. Version $ERWARTET laeuft." +else + printf '\n' + warnung "Die laufende Fassung stimmt nicht mit der erwarteten ueberein." + warnung "Vermutlich hat der Bau den alten Behaelter stehen lassen:" + printf ' docker compose up -d --build --force-recreate app\n' +fi + +printf '\n Zustand: docker compose ps\n' +printf ' Log: docker compose logs app --tail 50\n' +printf ' Zurueck: git checkout %s && docker compose up -d --build\n\n' "$JETZT" + +exit 0 +} diff --git a/src/lib/version.ts b/src/lib/version.ts index e9ef270..95253f1 100644 --- a/src/lib/version.ts +++ b/src/lib/version.ts @@ -17,7 +17,7 @@ */ /** Muss zur "version" in der package.json passen. */ -export const VERSION = '1.0.4' +export const VERSION = '1.0.5' /** * Kurzer Git-Stand, beim Bauen hineingereicht: