Baustein 1: Grundgeruest, Datenbank, Anmeldung, zwei Rollen
Legt das Fundament der Lern-Bar an: - Next.js 15 (App Router), React 19, TypeScript, ESLint, Vitest - SQLite ueber Drizzle hinter einer Repository-Schicht - Eigener Migrationslauf mit Pruefsummen, beim Start angewendet - Argon2id, serverseitige Sitzungen, HttpOnly-Cookie, SameSite=Lax - Rollen Ausbilder und Azubi, Rechteregeln als reine Funktionen - Verwaltung: Azubikonto und Ausbildung anlegen, Passwort, Sperren - Gestaltung Petrol/Bernstein, helle und dunkle Darstellung - Dockerfile, docker-compose, Lebenszeichen, update.sh mit Rueckfall - Sicherung und Wiederherstellung als Shell-Skripte - 29 Tests fuer Rechte, Zeitrechnung und Migrationslauf Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env bash
|
||||
# ---------------------------------------------------------------------------
|
||||
# Lern-Bar aktualisieren – für den Linux-Server, auf dem der Container läuft.
|
||||
#
|
||||
# ./update.sh Quelltext holen, neu bauen, neu starten
|
||||
# ./update.sh --ohne-git nur neu bauen und neu starten
|
||||
# ./update.sh --ohne-sicherung Sicherung überspringen (nicht empfohlen)
|
||||
# ./update.sh --zweig main einen bestimmten Git-Zweig holen
|
||||
#
|
||||
# Ablauf: Sicherung → Quelltext holen → Abbild bauen → umschalten →
|
||||
# Lebenszeichen prüfen. Klappt das Lebenszeichen nicht, wird automatisch auf
|
||||
# das vorherige Abbild zurückgeschaltet.
|
||||
# ---------------------------------------------------------------------------
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$(readlink -f "$0")")"
|
||||
|
||||
DIENST="lernbar"
|
||||
ABBILD="lernbar:lokal"
|
||||
ZWEIG=""
|
||||
MIT_GIT=1
|
||||
MIT_SICHERUNG=1
|
||||
WARTEZEIT=90
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--ohne-git) MIT_GIT=0 ;;
|
||||
--ohne-sicherung) MIT_SICHERUNG=0 ;;
|
||||
--zweig)
|
||||
ZWEIG="${2:-}"
|
||||
shift
|
||||
;;
|
||||
--hilfe | -h)
|
||||
sed -n '2,18p' "$0"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unbekannte Angabe: $1 (--hilfe zeigt die Möglichkeiten)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# --- Docker-Compose-Aufruf ermitteln ---------------------------------------
|
||||
if docker compose version >/dev/null 2>&1; then
|
||||
COMPOSE="docker compose"
|
||||
elif command -v docker-compose >/dev/null 2>&1; then
|
||||
COMPOSE="docker-compose"
|
||||
else
|
||||
echo "Weder 'docker compose' noch 'docker-compose' gefunden." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
meldung() { printf '\n\033[1;36m▸ %s\033[0m\n' "$1"; }
|
||||
warnung() { printf '\033[1;33m! %s\033[0m\n' "$1"; }
|
||||
fehler() { printf '\033[1;31m✗ %s\033[0m\n' "$1" >&2; }
|
||||
fertig() { printf '\033[1;32m✓ %s\033[0m\n' "$1"; }
|
||||
|
||||
# --- 1. Sicherung ----------------------------------------------------------
|
||||
if [ "$MIT_SICHERUNG" -eq 1 ]; then
|
||||
meldung "Sicherung anlegen"
|
||||
if $COMPOSE ps --status running --services 2>/dev/null | grep -qx "$DIENST"; then
|
||||
ARCHIV="$($COMPOSE exec -T "$DIENST" /app/werkzeuge/sicherung.sh /daten/sicherungen | tail -1)"
|
||||
fertig "Sicherung liegt im Volume unter ${ARCHIV}"
|
||||
else
|
||||
warnung "Container läuft nicht – Sicherung übersprungen."
|
||||
fi
|
||||
else
|
||||
warnung "Sicherung auf Wunsch übersprungen."
|
||||
fi
|
||||
|
||||
# --- 2. Quelltext holen ----------------------------------------------------
|
||||
VORHER_STAND=""
|
||||
if [ "$MIT_GIT" -eq 1 ]; then
|
||||
if [ -d .git ]; then
|
||||
meldung "Quelltext holen"
|
||||
VORHER_STAND="$(git rev-parse HEAD)"
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
fehler "Im Arbeitsverzeichnis liegen Änderungen. Bitte erst aufräumen (git status)."
|
||||
exit 1
|
||||
fi
|
||||
git fetch --prune origin
|
||||
if [ -n "$ZWEIG" ]; then
|
||||
git checkout "$ZWEIG"
|
||||
fi
|
||||
AKTUELLER_ZWEIG="$(git rev-parse --abbrev-ref HEAD)"
|
||||
git merge --ff-only "origin/$AKTUELLER_ZWEIG"
|
||||
fertig "Stand: $(git rev-parse --short HEAD) – $(git log -1 --pretty=%s)"
|
||||
else
|
||||
warnung "Kein Git-Repository – Quelltext bleibt, wie er ist."
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- 3. Vorheriges Abbild sichern ------------------------------------------
|
||||
ALTE_ID="$(docker image inspect "$ABBILD" --format '{{.Id}}' 2>/dev/null || true)"
|
||||
if [ -n "$ALTE_ID" ]; then
|
||||
docker tag "$ALTE_ID" "lernbar:vorher"
|
||||
fi
|
||||
|
||||
# --- 4. Bauen --------------------------------------------------------------
|
||||
meldung "Abbild bauen"
|
||||
if ! $COMPOSE build --pull "$DIENST"; then
|
||||
fehler "Der Bau ist fehlgeschlagen. Es läuft weiterhin die alte Fassung."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- 5. Umschalten ---------------------------------------------------------
|
||||
meldung "Neu starten"
|
||||
$COMPOSE up -d "$DIENST"
|
||||
|
||||
# --- 6. Lebenszeichen ------------------------------------------------------
|
||||
meldung "Auf das Lebenszeichen warten (bis zu ${WARTEZEIT}s)"
|
||||
GESUND=0
|
||||
for _ in $(seq 1 "$WARTEZEIT"); do
|
||||
ZUSTAND="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}kein-test{{end}}' "$DIENST" 2>/dev/null || echo unbekannt)"
|
||||
case "$ZUSTAND" in
|
||||
healthy | kein-test)
|
||||
GESUND=1
|
||||
break
|
||||
;;
|
||||
unhealthy)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [ "$GESUND" -ne 1 ]; then
|
||||
fehler "Die neue Fassung meldet sich nicht gesund. Ich schalte zurück."
|
||||
docker logs --tail 60 "$DIENST" || true
|
||||
|
||||
if [ -n "$ALTE_ID" ]; then
|
||||
docker tag "lernbar:vorher" "$ABBILD"
|
||||
$COMPOSE up -d --no-build "$DIENST"
|
||||
warnung "Zurückgeschaltet auf das vorherige Abbild."
|
||||
fi
|
||||
if [ -n "$VORHER_STAND" ]; then
|
||||
warnung "Quelltext steht auf einem neueren Stand. Zurück mit: git reset --hard $VORHER_STAND"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- 7. Aufräumen ----------------------------------------------------------
|
||||
meldung "Aufräumen"
|
||||
docker image prune -f --filter "label=org.opencontainers.image.title=lernbar" >/dev/null 2>&1 || true
|
||||
docker image rm "lernbar:vorher" >/dev/null 2>&1 || true
|
||||
|
||||
fertig "Lern-Bar läuft in der neuen Fassung."
|
||||
$COMPOSE ps "$DIENST"
|
||||
Reference in New Issue
Block a user