Files
Florian WildeandClaude Opus 5 f91d015cc4 feat(betrieb): Abbild, Compose-Stapel und Betriebsskripte
Dreistufiges Dockerfile mit standalone-Ausgabe; im Laufzeitabbild liegen
weder Quelltext noch Bauwerkzeuge, und es läuft unter einem eigenen
Benutzer.

Die Migrationen erledigt ein eigener Einmaldienst aus der Bau-Stufe. Der
Versuch, nur die Prisma-CLI ins Laufzeitabbild zu kopieren, scheiterte an
ihren transitiven Modulen; sie alle mitzunehmen hätte das Abbild
aufgebläht. Der Dienst läuft vor der App und muss sauber durchlaufen.

Zwei Stellen mussten faul werden, weil Next beim Bauen Seitendaten
sammelt und dabei Module lädt, die es zur Bauzeit noch ohne
Umgebungsvariablen gibt:
- Der Prisma-Client entsteht erst beim ersten Zugriff, über einen Proxy.
- Die Auth.js-Konfiguration wird als Funktion übergeben.
Ebenso liest prisma.config.ts die URL über process.env statt über env(),
das beim Bauen ohne Datenbank abbricht.

Dazu backup.sh, update.sh mit Rückrollpfad und scripts/release.sh. Der
Dump wurde nachweislich in eine Wegwerf-Datenbank zurückgespielt und
stimmt zeilengenau mit dem Original überein. Alle Skripte sind
shellcheck-sauber.

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

108 lines
3.1 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Sichert Datenbank, .env und Uploads des Planers.
# Wiederherstellung: siehe README.md, Abschnitt "Sicherung und Wiederherstellung".
set -euo pipefail
WURZEL="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$WURZEL"
ZIEL="${WURZEL}/sicherungen"
AUFBEWAHRUNG_TAGE=30
NUR_DATENBANK=0
hilfe() {
cat <<'ENDE'
Verwendung: backup.sh [Optionen]
--ziel <Pfad> Ablageort der Sicherung (Standard: ./sicherungen)
--tage <Anzahl> Aufbewahrung in Tagen (Standard: 30, 0 = unbegrenzt)
--nur-datenbank Nur den Datenbank-Dump erzeugen
--hilfe Diese Übersicht
Erzeugt je Lauf zwei Dateien:
planer-db-<Zeitstempel>.dump Datenbank im Custom-Format von pg_dump
planer-dateien-<Zeitstempel>.tar.gz .env und der Ordner uploads
ENDE
}
meldung() { printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1"; }
fehler() {
printf '%s Fehler: %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" >&2
exit 1
}
while [ $# -gt 0 ]; do
case "$1" in
--ziel)
ZIEL="${2:-}"
[ -n "$ZIEL" ] || fehler '--ziel braucht einen Pfad.'
shift 2
;;
--tage)
AUFBEWAHRUNG_TAGE="${2:-}"
case "$AUFBEWAHRUNG_TAGE" in
''|*[!0-9]*) fehler '--tage braucht eine ganze Zahl.' ;;
esac
shift 2
;;
--nur-datenbank)
NUR_DATENBANK=1
shift
;;
--hilfe|-h)
hilfe
exit 0
;;
*) fehler "Unbekannte Option '$1'. 'backup.sh --hilfe' zeigt die Übersicht." ;;
esac
done
command -v docker >/dev/null 2>&1 || fehler 'docker fehlt.'
[ -f "$WURZEL/.env" ] || fehler '.env fehlt – im Projektverzeichnis aufrufen.'
# Nur die benötigten Werte lesen, nicht die ganze Datei ausführen.
lies_env() {
local schluessel="$1" standard="${2:-}"
local wert
wert="$(grep -E "^${schluessel}=" "$WURZEL/.env" | tail -1 | cut -d= -f2- || true)"
wert="${wert%\"}"
wert="${wert#\"}"
printf '%s' "${wert:-$standard}"
}
PG_BENUTZER="$(lies_env POSTGRES_USER planer)"
PG_DATENBANK="$(lies_env POSTGRES_DB planer)"
DB_CONTAINER="$(docker compose ps -q db)"
[ -n "$DB_CONTAINER" ] || fehler 'Der Datenbank-Container läuft nicht. Erst "docker compose up -d db".'
ZEITSTEMPEL="$(date '+%Y-%m-%d_%H-%M-%S')"
mkdir -p "$ZIEL"
DUMP="$ZIEL/planer-db-${ZEITSTEMPEL}.dump"
meldung "Datenbank sichern nach $DUMP"
# -Fc ist das Custom-Format: komprimiert und mit pg_restore selektiv einspielbar.
docker exec "$DB_CONTAINER" pg_dump -U "$PG_BENUTZER" -d "$PG_DATENBANK" -Fc > "$DUMP"
if [ ! -s "$DUMP" ]; then
rm -f "$DUMP"
fehler 'Der Dump ist leer geblieben.'
fi
meldung "Datenbank gesichert ($(du -h "$DUMP" | cut -f1))."
if [ "$NUR_DATENBANK" -eq 0 ]; then
DATEIEN="$ZIEL/planer-dateien-${ZEITSTEMPEL}.tar.gz"
meldung "Dateien sichern nach $DATEIEN"
ZU_SICHERN=(.env)
[ -d "$WURZEL/uploads" ] && ZU_SICHERN+=(uploads)
tar -czf "$DATEIEN" -C "$WURZEL" "${ZU_SICHERN[@]}"
meldung "Dateien gesichert ($(du -h "$DATEIEN" | cut -f1))."
fi
if [ "$AUFBEWAHRUNG_TAGE" -gt 0 ]; then
meldung "Sicherungen älter als $AUFBEWAHRUNG_TAGE Tage entfernen."
find "$ZIEL" -maxdepth 1 -type f -name 'planer-*' -mtime "+$AUFBEWAHRUNG_TAGE" -print -delete
fi
meldung 'Sicherung abgeschlossen.'