#!/usr/bin/env bash # Wiederherstellung einer selbst gehosteten TeamVis-Instanz (Variante „eigene # DB“) aus den Sicherungen von backup.sh — in eine LEERE Datenbank: neuer # Host, neues Volume, Totalverlust. # # Im Instanz-Verzeichnis aufrufen (docker-compose.yml + .env aus dem # Passwort-Safe; ohne die originalen Secrets passen Keys und Passwörter nicht): # bash restore.sh /pfad/db-JJJJ-MM-TT-HHMM.dump /pfad/storage-JJJJ-MM-TT-HHMM.tar.gz # # Bricht ab, wenn die Datenbank schon TeamVis-Daten enthält oder # volumes/storage nicht leer ist. Über einen Bestand zurückspielen heißt also # bewusst: `docker compose down`, Volume `_db-data` löschen, # volumes/storage beiseiteschieben — erst dann dieses Skript. set -euo pipefail die() { echo "✗ $*" >&2; exit 1; } [ $# -eq 2 ] || die "Aufruf: restore.sh " DUMP="$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" TAR="$(cd "$(dirname "$2")" && pwd)/$(basename "$2")" [ -f "$DUMP" ] && [ -f "$TAR" ] || die "Dump oder Storage-Archiv fehlt." [ -f docker-compose.yml ] && [ -f .env ] || die "Kein Instanz-Verzeichnis (docker-compose.yml + .env)." if [ -d volumes/storage ] && [ -n "$(ls -A volumes/storage)" ]; then die "volumes/storage ist nicht leer." fi # psql/pg_restore im db-Container, als supabase_admin mit dem Passwort aus # dessen Umgebung — das Passwort taucht so in keiner Kommandozeile auf. dbsh() { docker compose exec -T db sh -c "$1"; } ADMIN='PGPASSWORD="$POSTGRES_PASSWORD" psql -h localhost -U supabase_admin -d "$POSTGRES_DB" -v ON_ERROR_STOP=1' echo "→ Datenbank starten …" docker compose up -d db until dbsh 'pg_isready -U postgres -d "$POSTGRES_DB"' >/dev/null 2>&1; do sleep 2; done # Das Image meldet sich bereit, bevor supabase_admin sein Passwort hat. until dbsh "$ADMIN -tAc 'select 1'" >/dev/null 2>&1; do sleep 2; done if [ "$(dbsh "$ADMIN -tAc \"select to_regclass('public.employees') is not null\"")" = "t" ]; then die "Die Datenbank enthält schon TeamVis-Daten — nur in eine leere einspielen." fi echo "→ Rollen (Passwörter, bypassrls) …" # Cluster-Eigenschaften, nicht im Dump. Wie install.sh: das Image setzt nur # supabase_admin, die übrigen Dienste melden sich mit POSTGRES_PASSWORD an. dbsh "$ADMIN -v pw=\"\$POSTGRES_PASSWORD\"" <<'SQL' alter role authenticator with login password :'pw'; alter role supabase_storage_admin with login password :'pw'; alter role supabase_auth_admin with login password :'pw'; alter role service_role bypassrls; SQL echo "→ Dump einspielen …" # Das Schema storage legt das Image selbst an (mit abhängigen Funktionen) — # dessen DROP/CREATE aus dem Dump überspringen, alles darin wird ersetzt. docker compose cp "$DUMP" db:/tmp/restore.dump >/dev/null dbsh 'pg_restore -l /tmp/restore.dump | grep -v " SCHEMA - storage " > /tmp/restore.list' dbsh 'PGPASSWORD="$POSTGRES_PASSWORD" pg_restore -h localhost -U supabase_admin -d "$POSTGRES_DB" --clean --if-exists --exit-on-error -L /tmp/restore.list /tmp/restore.dump' dbsh 'rm -f /tmp/restore.dump /tmp/restore.list' echo "→ Storage-Dateien …" mkdir -p volumes # Mit den erweiterten Attributen (Content-Type je Datei), siehe backup.sh. tar --xattrs --xattrs-include='user.*' -xzf "$TAR" -C volumes echo "→ Alle Dienste starten …" docker compose up -d echo "✔ Wiederhergestellt: $(dbsh "$ADMIN -tAc \"select count(*) || ' Karten, ' || (select count(*) from storage.objects) || ' Dateien' from public.employees\"")" echo " Prüfen: Storage gesund (docker compose ps), Karte und Fotos im Browser, Admin-Login."