commit adcaceb7ac8aa220f61ff0d62db08a3bb6610980 Author: thomas Date: Tue Jul 1 15:20:17 2025 +0200 initial commit diff --git a/Einzelne_Restore_Scripte/backup_config.sh b/Einzelne_Restore_Scripte/backup_config.sh new file mode 100755 index 0000000..3c8edf6 --- /dev/null +++ b/Einzelne_Restore_Scripte/backup_config.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +if [ -z "$1" ]; then + echo "Nutzung: $0 (docker-compose.yml|nginx.conf)" + exit 1 +fi + +DATEI="$1" +TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") +BACKUP_DIR="/home/thomas/backup_images/${TIMESTAMP}_config_${DATEI}" +mkdir -p "$BACKUP_DIR" + +if [ "$DATEI" = "docker-compose.yml" ]; then + SRC="/home/thomas/docker-compose.yml" +elif [ "$DATEI" = "nginx.conf" ]; then + SRC="/home/thomas/container/nginx/nginx.conf" +else + echo "Unbekannte Datei: $DATEI" + exit 1 +fi + +cp "$SRC" "$BACKUP_DIR/" +echo "Config-Backup für $DATEI gespeichert unter $BACKUP_DIR/" \ No newline at end of file diff --git a/Einzelne_Restore_Scripte/backup_db.sh b/Einzelne_Restore_Scripte/backup_db.sh new file mode 100755 index 0000000..a43e41c --- /dev/null +++ b/Einzelne_Restore_Scripte/backup_db.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +if [ -z "$1" ]; then + echo "Nutzung: $0 " + exit 1 +fi + +CONTAINER="$1" +DB_UEBERSICHT="/home/thomas/db_uebersicht.md" +read USER PASS < <(tail -n +5 "$DB_UEBERSICHT" | grep "| $CONTAINER " | awk -F '|' '{print $5, $6}' | xargs) + +TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") +BACKUP_DIR="/home/thomas/backup_images/${TIMESTAMP}_db_${CONTAINER}" +mkdir -p "$BACKUP_DIR" + +echo "Erstelle Dump für $CONTAINER ($USER)..." +docker exec $CONTAINER mysqldump -u $USER -p$PASS --all-databases --single-transaction --quick --lock-tables=false > "$BACKUP_DIR/${CONTAINER}.sql" \ + && echo "DB-Backup für $CONTAINER gespeichert unter $BACKUP_DIR/${CONTAINER}.sql" \ + || echo "Fehler beim Dump von $CONTAINER" \ No newline at end of file diff --git a/Einzelne_Restore_Scripte/backup_image.sh b/Einzelne_Restore_Scripte/backup_image.sh new file mode 100755 index 0000000..3f972c8 --- /dev/null +++ b/Einzelne_Restore_Scripte/backup_image.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +if [ -z "$1" ]; then + echo "Nutzung: $0 (z.B. nginx:latest)" + exit 1 +fi + +IMAGE="$1" +SAFE_IMAGE=$(echo $IMAGE | tr '/:' '_') +TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") +BACKUP_DIR="/home/thomas/backup_images/${TIMESTAMP}_image_${SAFE_IMAGE}" +mkdir -p "$BACKUP_DIR" + +docker save "$IMAGE" | gzip > "$BACKUP_DIR/${SAFE_IMAGE}.tar.gz" +echo "Image-Backup für $IMAGE gespeichert unter $BACKUP_DIR/${SAFE_IMAGE}.tar.gz" \ No newline at end of file diff --git a/Einzelne_Restore_Scripte/backup_media.sh b/Einzelne_Restore_Scripte/backup_media.sh new file mode 100755 index 0000000..30c04d1 --- /dev/null +++ b/Einzelne_Restore_Scripte/backup_media.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +if [ -z "$1" ]; then + echo "Nutzung: $0 (webshop|ssv)" + exit 1 +fi + +PROJEKT="$1" +TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") +BACKUP_DIR="/home/thomas/backup_images/${TIMESTAMP}_media_${PROJEKT}" +mkdir -p "$BACKUP_DIR" + +if [ "$PROJEKT" = "webshop" ]; then + SRC="/home/thomas/container/webshop/cleanbuild/media" +elif [ "$PROJEKT" = "ssv" ]; then + SRC="/home/thomas/container/ssv/html/media" +else + echo "Unbekanntes Projekt: $PROJEKT" + exit 1 +fi + +tar czf "$BACKUP_DIR/${PROJEKT}_media.tar.gz" -C "$SRC" . +echo "Media-Backup für $PROJEKT gespeichert unter $BACKUP_DIR/${PROJEKT}_media.tar.gz" \ No newline at end of file diff --git a/Einzelne_Restore_Scripte/backup_staticfiles.sh b/Einzelne_Restore_Scripte/backup_staticfiles.sh new file mode 100755 index 0000000..c7039d2 --- /dev/null +++ b/Einzelne_Restore_Scripte/backup_staticfiles.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +if [ -z "$1" ]; then + echo "Nutzung: $0 (webshop|ssv)" + exit 1 +fi + +PROJEKT="$1" +TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") +BACKUP_DIR="/home/thomas/backup_images/${TIMESTAMP}_static_${PROJEKT}" +mkdir -p "$BACKUP_DIR" + +if [ "$PROJEKT" = "webshop" ]; then + SRC="/home/thomas/container/webshop/cleanbuild/staticfiles" +elif [ "$PROJEKT" = "ssv" ]; then + SRC="/home/thomas/container/ssv/html/staticfiles" +else + echo "Unbekanntes Projekt: $PROJEKT" + exit 1 +fi + +tar czf "$BACKUP_DIR/${PROJEKT}_staticfiles.tar.gz" -C "$SRC" . +echo "Staticfiles-Backup für $PROJEKT gespeichert unter $BACKUP_DIR/${PROJEKT}_staticfiles.tar.gz" \ No newline at end of file diff --git a/Einzelne_Restore_Scripte/restore_config.sh b/Einzelne_Restore_Scripte/restore_config.sh new file mode 100755 index 0000000..a3ca22b --- /dev/null +++ b/Einzelne_Restore_Scripte/restore_config.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +if [ -z "$1" ] || [ -z "$2" ]; then + echo "Nutzung: $0 (docker-compose.yml|nginx.conf)" + exit 1 +fi + +BACKUP_DIR="/home/thomas/backup_images/$1" +DATEI="$2" + +if [ "$DATEI" = "docker-compose.yml" ]; then + ZIEL="/home/thomas/docker-compose.yml" +elif [ "$DATEI" = "nginx.conf" ]; then + ZIEL="/home/thomas/container/nginx/nginx.conf" +else + echo "Unbekannte Datei: $DATEI" + exit 1 +fi + +if [ ! -f "$BACKUP_DIR/$DATEI" ]; then + echo "Backup nicht gefunden: $BACKUP_DIR/$DATEI" + exit 1 +fi + +echo "Stelle $DATEI wieder her..." +cp "$BACKUP_DIR/$DATEI" "$ZIEL" \ No newline at end of file diff --git a/Einzelne_Restore_Scripte/restore_db.sh b/Einzelne_Restore_Scripte/restore_db.sh new file mode 100755 index 0000000..31a1b9d --- /dev/null +++ b/Einzelne_Restore_Scripte/restore_db.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +if [ -z "$1" ] || [ -z "$2" ]; then + echo "Nutzung: $0 " + exit 1 +fi + +BACKUP_DIR="/home/thomas/backup_images/$1/db_dumps" +DB_UEBERSICHT="/home/thomas/db_uebersicht.md" +CONTAINER="$2" + +# Hole User und Passwort aus Übersicht +read USER PASS < <(tail -n +5 "$DB_UEBERSICHT" | grep "| $CONTAINER " | awk -F '|' '{print $5, $6}' | xargs) + +DUMP_FILE="$BACKUP_DIR/${CONTAINER}.sql" +if [ ! -f "$DUMP_FILE" ]; then + echo "Dump nicht gefunden: $DUMP_FILE" + exit 1 +fi + +echo "Importiere Dump für $CONTAINER ($USER)..." +cat "$DUMP_FILE" | docker exec -i $CONTAINER mysql -u $USER -p$PASS || echo "Fehler beim Import in $CONTAINER" \ No newline at end of file diff --git a/Einzelne_Restore_Scripte/restore_image.sh b/Einzelne_Restore_Scripte/restore_image.sh new file mode 100755 index 0000000..3d7d18e --- /dev/null +++ b/Einzelne_Restore_Scripte/restore_image.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +if [ -z "$1" ] || [ -z "$2" ]; then + echo "Nutzung: $0 (z.B. nginx_latest)" + exit 1 +fi + +BACKUP_DIR="/home/thomas/backup_images/$1/images" +IMAGE="$2" +TARFILE="$BACKUP_DIR/${IMAGE}.tar.gz" + +if [ ! -f "$TARFILE" ]; then + echo "Backup nicht gefunden: $TARFILE" + exit 1 +fi + +echo "Importiere Image $IMAGE..." +gunzip -c "$TARFILE" | docker load \ No newline at end of file diff --git a/Einzelne_Restore_Scripte/restore_media.sh b/Einzelne_Restore_Scripte/restore_media.sh new file mode 100755 index 0000000..706485d --- /dev/null +++ b/Einzelne_Restore_Scripte/restore_media.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +if [ -z "$1" ] || [ -z "$2" ]; then + echo "Nutzung: $0 (webshop|ssv)" + exit 1 +fi + +BACKUP_DIR="/home/thomas/backup_images/$1/volumes" +PROJEKT="$2" + +if [ "$PROJEKT" = "webshop" ]; then + TARFILE="$BACKUP_DIR/webshop_media.tar.gz" + ZIEL="/home/thomas/container/webshop/cleanbuild" +elif [ "$PROJEKT" = "ssv" ]; then + TARFILE="$BACKUP_DIR/ssv_media.tar.gz" + ZIEL="/home/thomas/container/ssv/html" +else + echo "Unbekanntes Projekt: $PROJEKT" + exit 1 +fi + +if [ ! -f "$TARFILE" ]; then + echo "Backup nicht gefunden: $TARFILE" + exit 1 +fi + +echo "Stelle Media für $PROJEKT wieder her..." +tar xzf "$TARFILE" -C "$ZIEL" \ No newline at end of file diff --git a/Einzelne_Restore_Scripte/restore_staticfiles.sh b/Einzelne_Restore_Scripte/restore_staticfiles.sh new file mode 100755 index 0000000..c95eae9 --- /dev/null +++ b/Einzelne_Restore_Scripte/restore_staticfiles.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +if [ -z "$1" ] || [ -z "$2" ]; then + echo "Nutzung: $0 (webshop|ssv)" + exit 1 +fi + +BACKUP_DIR="/home/thomas/backup_images/$1/volumes" +PROJEKT="$2" + +if [ "$PROJEKT" = "webshop" ]; then + TARFILE="$BACKUP_DIR/webshop_staticfiles.tar.gz" + ZIEL="/home/thomas/container/webshop/cleanbuild" +elif [ "$PROJEKT" = "ssv" ]; then + TARFILE="$BACKUP_DIR/ssv_staticfiles.tar.gz" + ZIEL="/home/thomas/container/ssv/html" +else + echo "Unbekanntes Projekt: $PROJEKT" + exit 1 +fi + +if [ ! -f "$TARFILE" ]; then + echo "Backup nicht gefunden: $TARFILE" + exit 1 +fi + +echo "Stelle Staticfiles für $PROJEKT wieder her..." +tar xzf "$TARFILE" -C "$ZIEL" \ No newline at end of file diff --git a/NAS_CREDENTIALS.txt b/NAS_CREDENTIALS.txt new file mode 100644 index 0000000..89e8f1d --- /dev/null +++ b/NAS_CREDENTIALS.txt @@ -0,0 +1,4 @@ +# Zugangsdaten für WD MyCloud EX2 Ultra NAS +user=backup +password=backupnuk +# Freigabe: //192.168.178.101/Backup \ No newline at end of file diff --git a/SKRIPTE_INFO.md b/SKRIPTE_INFO.md new file mode 100644 index 0000000..c9cf79a --- /dev/null +++ b/SKRIPTE_INFO.md @@ -0,0 +1,64 @@ +# Übersicht der Docker- und Gitea-Skripte + +## Hauptskripte +- **backup_docker.sh**: Erstellt ein vollständiges Backup aller Docker-Images, Volumes, Media/Staticfiles, Konfigurationsdateien und MariaDB-Dumps. Das Backup wird in einem Zeitstempel-Ordner unter `/home/thomas/backup_images/` abgelegt. +- **restore_docker.sh**: Stellt ein Backup aus einem Zeitstempel-Ordner komplett wieder her (Images, Volumes, DB-Dumps, Konfigs). +- **restore_menu.sh**: Interaktives Menü für alle Backup- und Restore-Funktionen (siehe unten). + +## Einzelne_Restore_Scripte +Alle Einzelskripte für gezielte Sicherung und Wiederherstellung liegen in `/home/thomas/scripte/Einzelne_Restore_Scripte/`: +- **backup_db.sh**: Sichert gezielt einen Dump einer einzelnen Datenbank (Container). +- **backup_media.sh**: Sichert gezielt die Media-Dateien eines Projekts (webshop|ssv). +- **backup_staticfiles.sh**: Sichert gezielt die Staticfiles eines Projekts (webshop|ssv). +- **backup_image.sh**: Sichert gezielt ein Docker-Image. +- **backup_config.sh**: Sichert gezielt docker-compose.yml oder nginx.conf. +- **restore_db.sh**: Stellt gezielt den Dump einer einzelnen Datenbank (Container) wieder her. +- **restore_media.sh**: Stellt nur die Media-Dateien eines Projekts (webshop|ssv) wieder her. +- **restore_staticfiles.sh**: Stellt nur die Staticfiles eines Projekts (webshop|ssv) wieder her. +- **restore_image.sh**: Importiert gezielt ein Docker-Image aus einem Backup. +- **restore_config.sh**: Stellt gezielt docker-compose.yml oder nginx.conf wieder her. + +## Wartungsscripte +Alle Wartungs- und Hilfsskripte liegen in `/home/thomas/scripte/Wartungsscripte/`: +- **docker_cleanup.sh**: Entfernt nicht mehr verwendete Docker-Objekte (Container, Images, Volumes) nach Bestätigung. +- **cleanup_old_backups.sh**: Löscht alle Backup-Ordner in `/home/thomas/backup_images/`, die älter als 14 Tage sind. +- **db_dump_single.sh**: Erstellt einen SQL-Dump einer einzelnen MariaDB im laufenden Container. +- **docker_logs.sh**: Zeigt die letzten 100 Zeilen der Logs eines angegebenen Containers an. +- **docker_status.sh**: Zeigt alle laufenden und gestoppten Docker-Container an. + +## Git & Gitea Menü +- **git_gitea_menu.sh**: Interaktives Menü für lokale Git-Operationen und Gitea-API-Operationen. + - **Funktionen:** + - Repository klonen: Klont ein bestehendes Repo von Gitea oder einer anderen Quelle. + - Status anzeigen: Zeigt den aktuellen Git-Status im aktuellen Verzeichnis. + - Pull: Holt die neuesten Änderungen vom Remote-Repo. + - Push: Überträgt lokale Commits ins Remote-Repo. + - Branch wechseln: Wechselt oder erstellt einen Branch. + - Commit: Fügt Änderungen hinzu und erstellt einen Commit. + - Gitea: Repo-Liste anzeigen: Zeigt alle Repos des Users auf Gitea. + - Gitea: Neues Repo anlegen: Legt ein neues Repository auf Gitea an (Repo-Name wird validiert, Leerzeichen werden ersetzt). + - Gitea: Issue anzeigen: Zeigt ein Issue eines Repos an. + - Gitea: Issue erstellen: Erstellt ein neues Issue in einem Repo. + - **Folgen der Aktionen:** + - Repo anlegen: Das neue Repo erscheint sofort auf Gitea, kann geklont und genutzt werden. + - Issue erstellen: Das Issue ist sofort im Gitea-Webinterface sichtbar. + - Alle Aktionen sind sofort wirksam und können nicht rückgängig gemacht werden. + - Die Validierung verhindert ungültige Repo-Namen und sorgt für Kompatibilität mit Gitea. + - Mit "z" kann man jederzeit zurück ins Hauptmenü. + +## Nutzung des Backup & Restore Menüs +Starte das Menü mit: +```bash +/home/thomas/scripte/restore_menu.sh +``` +Dort kannst du alle Backup- und Restore-Funktionen bequem auswählen und ausführen. Im Menü gibt es jetzt auch einen Bereich für gezielte Sicherung einzelner Bereiche (DB, Media, Static, Image, Config). Die Zurück-Funktion ("z") ist überall verfügbar. + +## Erweiterte Best Practice (empfohlen) +- **Offsite-Backup:** + - Backups werden automatisch auf ein externes System (z.B. NAS, Cloud) kopiert. Beispiel: WD MyCloud EX2 Ultra unter 192.168.178.100. +- **Benachrichtigung:** + - Nach jedem Backup/Restore kann eine E-Mail oder Slack-Nachricht über Erfolg oder Fehler verschickt werden. +- **Regelmäßiger Restore-Test:** + - Ein Test-Restore-Skript prüft regelmäßig, ob Backups erfolgreich wiederhergestellt werden können. +- **Versionsverwaltung der Skripte:** + - Die Skripte selbst werden in einem Git-Repository versioniert (z.B. auf deinem Gitea-Server), um Änderungen nachverfolgen zu können. \ No newline at end of file diff --git a/Wartungsscripte/cleanup_old_backups.sh b/Wartungsscripte/cleanup_old_backups.sh new file mode 100755 index 0000000..1c39d6b --- /dev/null +++ b/Wartungsscripte/cleanup_old_backups.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +find /home/thomas/backup_images/* -maxdepth 0 -type d -mtime +14 -exec rm -rf {} \; +echo "Alte Backups (älter als 14 Tage) wurden gelöscht." \ No newline at end of file diff --git a/Wartungsscripte/db_dump_single.sh b/Wartungsscripte/db_dump_single.sh new file mode 100755 index 0000000..516b478 --- /dev/null +++ b/Wartungsscripte/db_dump_single.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +if [ -z "$1" ] || [ -z "$2" ]; then + echo "Nutzung: $0 " + exit 1 +fi + +CONTAINER=$1 +PASS=$2 +DATE=$(date +"%Y-%m-%d_%H-%M-%S") +BACKUP_DIR="/home/thomas/backup_images/single_db_dumps" +mkdir -p "$BACKUP_DIR" +docker exec $CONTAINER mysqldump -u root -p$PASS --all-databases --single-transaction --quick --lock-tables=false > "$BACKUP_DIR/${CONTAINER}_$DATE.sql" +echo "Dump gespeichert unter $BACKUP_DIR/${CONTAINER}_$DATE.sql" \ No newline at end of file diff --git a/Wartungsscripte/docker_cleanup.sh b/Wartungsscripte/docker_cleanup.sh new file mode 100755 index 0000000..4b1f250 --- /dev/null +++ b/Wartungsscripte/docker_cleanup.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +echo "Achtung: Dies entfernt alle nicht verwendeten Container, Images und Volumes!" +read -p "Fortfahren? (ja/nein): " confirm +if [ "$confirm" != "ja" ]; then + echo "Abgebrochen." + exit 0 +fi + +docker system prune -a --volumes \ No newline at end of file diff --git a/Wartungsscripte/docker_logs.sh b/Wartungsscripte/docker_logs.sh new file mode 100755 index 0000000..a215156 --- /dev/null +++ b/Wartungsscripte/docker_logs.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +if [ -z "$1" ]; then + echo "Bitte gib den Containernamen an!" + echo "Beispiel: $0 nginx" + exit 1 +fi + +docker logs --tail 100 $1 \ No newline at end of file diff --git a/Wartungsscripte/docker_status.sh b/Wartungsscripte/docker_status.sh new file mode 100755 index 0000000..27ca78a --- /dev/null +++ b/Wartungsscripte/docker_status.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +echo "Laufende Container:" +docker ps + +echo + +echo "Alle Container (inkl. gestoppte):" +docker ps -a \ No newline at end of file diff --git a/backup_docker.sh b/backup_docker.sh new file mode 100755 index 0000000..839b4d8 --- /dev/null +++ b/backup_docker.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# Backup-Skript für Docker-Umgebung nach Best Practice +# Sichert: Docker-Images, Volumes, Konfigs, MariaDB-Dumps (automatisch aus db_uebersicht.md) +# Ziel: /home/thomas/backup_images// + +set -e + +BACKUP_ROOT="/home/thomas/backup_images" +TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") +BACKUP_DIR="$BACKUP_ROOT/$TIMESTAMP" + +mkdir -p "$BACKUP_DIR" + +# 1. docker-compose.yml und wichtige Konfigs sichern +cp /home/thomas/docker-compose.yml "$BACKUP_DIR/" +cp /home/thomas/nginx_docker_setup.md "$BACKUP_DIR/" 2>/dev/null || true +cp /home/thomas/SYSTEM_INFO_DOCKER.txt "$BACKUP_DIR/" 2>/dev/null || true +cp /home/thomas/container/nginx/nginx.conf "$BACKUP_DIR/" 2>/dev/null || true + +# 2. Docker-Images sichern +mkdir -p "$BACKUP_DIR/images" +docker images --format '{{.Repository}}:{{.Tag}}' | grep -v "" | while read IMAGE; do + SAFE_IMAGE=$(echo $IMAGE | tr '/:' '_') + docker save "$IMAGE" | gzip > "$BACKUP_DIR/images/${SAFE_IMAGE}.tar.gz" +done + +# 3. MariaDB-Dumps automatisch aus db_uebersicht.md +mkdir -p "$BACKUP_DIR/db_dumps" +DB_UEBERSICHT="/home/thomas/db_uebersicht.md" +tail -n +5 "$DB_UEBERSICHT" | grep -v '^$' | while IFS='|' read -r _ app db_host port user pass _ _ _; do + db_host=$(echo "$db_host" | xargs) + user=$(echo "$user" | xargs) + pass=$(echo "$pass" | xargs) + if [[ -n "$db_host" && -n "$user" && -n "$pass" && "$db_host" != "DB-Host" ]]; then + DUMP_FILE="$BACKUP_DIR/db_dumps/${db_host}.sql" + echo "Erstelle Dump für $db_host ($user)..." + docker exec $db_host mysqldump -u $user -p$pass --all-databases --single-transaction --quick --lock-tables=false > "$DUMP_FILE" \ + || echo "Fehler beim Dump von $db_host" + fi +done + +# 4. Volumes & wichtige Daten sichern (ohne MariaDB-DB-Volumes) +mkdir -p "$BACKUP_DIR/volumes" +# Webshop Media/Static/Templates +cd /home/thomas/container/webshop/cleanbuild +tar czf "$BACKUP_DIR/volumes/webshop_media.tar.gz" media/ +tar czf "$BACKUP_DIR/volumes/webshop_staticfiles.tar.gz" staticfiles/ +tar czf "$BACKUP_DIR/volumes/webshop_templates.tar.gz" templates/ +# SSV Media/Static +cd /home/thomas/container/ssv/html +tar czf "$BACKUP_DIR/volumes/ssv_media.tar.gz" media/ +tar czf "$BACKUP_DIR/volumes/ssv_staticfiles.tar.gz" staticfiles/ + +# 5. Let's Encrypt Zertifikate sichern (optional, falls Rechte vorhanden) +if [ -d "/etc/letsencrypt/live/kasimirat.de" ]; then + mkdir -p "$BACKUP_DIR/letsencrypt" + cp -r /etc/letsencrypt/live/kasimirat.de "$BACKUP_DIR/letsencrypt/" 2>/dev/null || true + cp -r /etc/letsencrypt/archive/kasimirat.de "$BACKUP_DIR/letsencrypt/" 2>/dev/null || true +fi + +# 6. Container-Infos sichern +mkdir -p "$BACKUP_DIR/container_info" +docker ps -a > "$BACKUP_DIR/container_info/docker_ps_a.txt" +docker inspect $(docker ps -aq) > "$BACKUP_DIR/container_info/docker_inspect.json" + +echo "Backup abgeschlossen: $BACKUP_DIR" \ No newline at end of file diff --git a/git_gitea_menu.sh b/git_gitea_menu.sh new file mode 100755 index 0000000..8bc6355 --- /dev/null +++ b/git_gitea_menu.sh @@ -0,0 +1,123 @@ +#!/bin/bash + +GITEA_URL="https://gitea.kasimirat.de" +GITEA_USER="thomas" +GITEA_TOKEN="289ac11a4ad2938fa34e6baa29bec74bdbcbb57b" + +validate_repo_name() { + local name="$1" + # Leerzeichen durch Bindestrich ersetzen + name="${name// /-}" + # Prüfen auf erlaubte Zeichen + if [[ ! "$name" =~ ^[A-Za-z0-9._-]+$ ]]; then + echo "Ungültiger Repo-Name! Nur Buchstaben, Zahlen, Bindestrich (-), Unterstrich (_) und Punkt (.) sind erlaubt." + return 1 + fi + echo "$name" +} + +while true; do + clear + echo "==== Git & Gitea Menü ====" + echo "1) Repository klonen" + echo "2) Status anzeigen" + echo "3) Pull" + echo "4) Push" + echo "5) Branch wechseln" + echo "6) Commit" + echo "7) Gitea: Repo-Liste anzeigen" + echo "8) Gitea: Neues Repo anlegen" + echo "9) Gitea: Issue anzeigen" + echo "10) Gitea: Issue erstellen" + echo "q) Beenden" + echo + read -p "Bitte Auswahl eingeben: " auswahl + + case $auswahl in + 1) + read -p "Repo-URL (z.B. https://gitea.kasimirat.de/thomas/repo.git, z für zurück): " url + [[ "$url" =~ ^[zZ]$ ]] && continue + repo_name=$(basename -s .git "$url") + valid_name=$(validate_repo_name "$repo_name") || { read -p "Weiter mit Enter..."; continue; } + url_valid=$(echo "$url" | sed "s|$repo_name|$valid_name|") + git clone "$url_valid" + read -p "Weiter mit Enter..." + ;; + 2) + git status + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && continue + ;; + 3) + git pull + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && continue + ;; + 4) + git push + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && continue + ;; + 5) + git branch + read -p "Branch-Name (z für zurück): " br + [[ "$br" =~ ^[zZ]$ ]] && continue + git checkout "$br" + read -p "Weiter mit Enter..." + ;; + 6) + git status + read -p "Commit-Message (z für zurück): " msg + [[ "$msg" =~ ^[zZ]$ ]] && continue + git add . + git commit -m "$msg" + read -p "Weiter mit Enter..." + ;; + 7) + curl -s -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user/repos" | jq '.[] | .full_name' + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && continue + ;; + 8) + read -p "Neuer Repo-Name (z für zurück): " repo + [[ "$repo" =~ ^[zZ]$ ]] && continue + valid_name=$(validate_repo_name "$repo") || { read -p "Weiter mit Enter..."; continue; } + curl -s -X POST -H "Content-Type: application/json" -H "Authorization: token $GITEA_TOKEN" \ + -d '{"name":"'$valid_name'","private":false}' "$GITEA_URL/api/v1/user/repos" > /dev/null + echo "Repository mit dem Namen $valid_name wurde angelegt." + read -p "Weiter mit Enter..." + ;; + 9) + read -p "Repo (z.B. thomas/repo, z für zurück): " repo + [[ "$repo" =~ ^[zZ]$ ]] && continue + user_part=$(echo "$repo" | cut -d'/' -f1) + repo_part=$(echo "$repo" | cut -d'/' -f2) + valid_repo_part=$(validate_repo_name "$repo_part") || { read -p "Weiter mit Enter..."; continue; } + repo_valid="$user_part/$valid_repo_part" + read -p "Issue-Nummer (z für zurück): " num + [[ "$num" =~ ^[zZ]$ ]] && continue + curl -s -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/repos/$repo_valid/issues/$num" | jq + read -p "Weiter mit Enter..." + ;; + 10) + read -p "Repo (z.B. thomas/repo, z für zurück): " repo + [[ "$repo" =~ ^[zZ]$ ]] && continue + user_part=$(echo "$repo" | cut -d'/' -f1) + repo_part=$(echo "$repo" | cut -d'/' -f2) + valid_repo_part=$(validate_repo_name "$repo_part") || { read -p "Weiter mit Enter..."; continue; } + repo_valid="$user_part/$valid_repo_part" + read -p "Titel (z für zurück): " titel + [[ "$titel" =~ ^[zZ]$ ]] && continue + read -p "Beschreibung (z für zurück): " desc + [[ "$desc" =~ ^[zZ]$ ]] && continue + curl -s -X POST -H "Content-Type: application/json" -H "Authorization: token $GITEA_TOKEN" \ + -d '{"title":"'$titel'","body":"'$desc'"}' "$GITEA_URL/api/v1/repos/$repo_valid/issues" > /dev/null + echo "Issue erstellt." + read -p "Weiter mit Enter..." + ;; + q) + echo "Beende Menü." + exit 0 + ;; + *) + echo "Ungültige Auswahl." + read -p "Weiter mit Enter..." + ;; + esac +done \ No newline at end of file diff --git a/git_gitea_upload.sh b/git_gitea_upload.sh new file mode 100755 index 0000000..c84e3d5 --- /dev/null +++ b/git_gitea_upload.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +GITEA_URL="https://gitea.kasimirat.de" +GITEA_USER="thomas" +GITEA_TOKEN="289ac11a4ad2938fa34e6baa29bec74bdbcbb57b" + +read -p "Pfad zum lokalen Verzeichnis (z für Abbruch): " DIR +[[ "$DIR" =~ ^[zZ]$ ]] && exit 0 +if [ ! -d "$DIR" ]; then + echo "Verzeichnis nicht gefunden!" + exit 1 +fi +cd "$DIR" + +read -p "Neuer Repo-Name auf Gitea (z für Abbruch): " REPO +[[ "$REPO" =~ ^[zZ]$ ]] && exit 0 +# Repo-Name validieren (wie im Menü) +REPO_VALID=$(echo "$REPO" | sed 's/ /-/g') +if [[ ! "$REPO_VALID" =~ ^[A-Za-z0-9._-]+$ ]]; then + echo "Ungültiger Repo-Name! Nur Buchstaben, Zahlen, Bindestrich (-), Unterstrich (_) und Punkt (.) sind erlaubt." + exit 1 +fi + +# Repo per API anlegen +ANTWORT=$(curl -s -X POST -H "Content-Type: application/json" -H "Authorization: token $GITEA_TOKEN" \ + -d '{"name":"'$REPO_VALID'","private":false}' "$GITEA_URL/api/v1/user/repos") +if echo "$ANTWORT" | grep -q '"id"'; then + echo "Gitea-Repo $REPO_VALID wurde angelegt." +else + echo "Fehler beim Anlegen des Repos: $ANTWORT" + exit 1 +fi + +# Git-Repo initialisieren (falls noch nicht vorhanden) +if [ ! -d .git ]; then + git init +fi + +git remote remove origin 2>/dev/null +GITURL="$GITEA_URL/$GITEA_USER/$REPO_VALID.git" +git remote add origin "$GITURL" + +git add . +read -p "Commit-Message: " MSG +[[ "$MSG" =~ ^[zZ]$ ]] && exit 0 +git commit -m "$MSG" + +git branch -M main 2>/dev/null + +git push -u origin main + +echo "Upload abgeschlossen. Repo: $GITURL" \ No newline at end of file diff --git a/restore_docker.sh b/restore_docker.sh new file mode 100755 index 0000000..7f780cb --- /dev/null +++ b/restore_docker.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# Restore-Skript für Docker-Backups +# Nutzt einen Backup-Ordner aus /home/thomas/backup_images/ + +if [ -z "$1" ]; then + echo "Bitte gib den Backup-Ordner als Argument an!" + echo "Beispiel: $0 2024-07-01_12-00-00" + exit 1 +fi + +BACKUP_ROOT="/home/thomas/backup_images" +BACKUP_DIR="$BACKUP_ROOT/$1" + +if [ ! -d "$BACKUP_DIR" ]; then + echo "Backup-Ordner nicht gefunden: $BACKUP_DIR" + exit 1 +fi + +# 1. Images wiederherstellen +if [ -d "$BACKUP_DIR/images" ]; then + for IMG in "$BACKUP_DIR/images"/*.tar.gz; do + echo "Importiere Image: $IMG" + gunzip -c "$IMG" | docker load + done +fi + +# 2. Volumes wiederherstellen (Achtung: überschreibt aktuelle Daten!) +# Beispiel für Webshop Media/Static/Templates +if [ -f "$BACKUP_DIR/volumes/webshop_media.tar.gz" ]; then + echo "Stelle Webshop Media wieder her..." + tar xzf "$BACKUP_DIR/volumes/webshop_media.tar.gz" -C /home/thomas/container/webshop/cleanbuild +fi +if [ -f "$BACKUP_DIR/volumes/webshop_staticfiles.tar.gz" ]; then + echo "Stelle Webshop Staticfiles wieder her..." + tar xzf "$BACKUP_DIR/volumes/webshop_staticfiles.tar.gz" -C /home/thomas/container/webshop/cleanbuild +fi +if [ -f "$BACKUP_DIR/volumes/webshop_templates.tar.gz" ]; then + echo "Stelle Webshop Templates wieder her..." + tar xzf "$BACKUP_DIR/volumes/webshop_templates.tar.gz" -C /home/thomas/container/webshop/cleanbuild +fi +# SSV Media/Static +if [ -f "$BACKUP_DIR/volumes/ssv_media.tar.gz" ]; then + echo "Stelle SSV Media wieder her..." + tar xzf "$BACKUP_DIR/volumes/ssv_media.tar.gz" -C /home/thomas/container/ssv/html +fi +if [ -f "$BACKUP_DIR/volumes/ssv_staticfiles.tar.gz" ]; then + echo "Stelle SSV Staticfiles wieder her..." + tar xzf "$BACKUP_DIR/volumes/ssv_staticfiles.tar.gz" -C /home/thomas/container/ssv/html +fi + +# 3. DB-Dumps importieren (automatisch aus db_uebersicht.md) +DB_UEBERSICHT="/home/thomas/db_uebersicht.md" +DUMP_DIR="$BACKUP_DIR/db_dumps" +if [ -d "$DUMP_DIR" ]; then + tail -n +5 "$DB_UEBERSICHT" | grep -v '^$' | while IFS='|' read -r _ app db_host port user pass _ _ _; do + db_host=$(echo "$db_host" | xargs) + user=$(echo "$user" | xargs) + pass=$(echo "$pass" | xargs) + if [[ -n "$db_host" && -n "$user" && -n "$pass" && "$db_host" != "DB-Host" ]]; then + DUMP_FILE="$DUMP_DIR/${db_host}.sql" + if [ -f "$DUMP_FILE" ]; then + echo "Importiere Dump für $db_host ($user)..." + cat "$DUMP_FILE" | docker exec -i $db_host mysql -u $user -p$pass || echo "Fehler beim Import in $db_host" + else + echo "Kein Dump für $db_host gefunden: $DUMP_FILE" + fi + fi + done +fi + +# 4. Konfigs wiederherstellen (manuell prüfen!) +# cp "$BACKUP_DIR/docker-compose.yml" /home/thomas/docker-compose.yml +# cp "$BACKUP_DIR/nginx.conf" /home/thomas/container/nginx/nginx.conf + +# 5. Zertifikate (optional) +# cp -r "$BACKUP_DIR/letsencrypt"/* /etc/letsencrypt/ + +echo "Restore abgeschlossen. Bitte prüfe die Konfigurationen und starte ggf. die Container neu!" \ No newline at end of file diff --git a/restore_menu.sh b/restore_menu.sh new file mode 100755 index 0000000..a603332 --- /dev/null +++ b/restore_menu.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +while true; do + clear + echo "==== Backup & Restore Menü ====" + echo "1) Komplettes Backup erstellen" + echo "2) Komplettes Restore durchführen" + echo "3) Einzelne Bereiche sichern (DB, Media, Static, Image, Config)" + echo "4) Einzelne Datenbank wiederherstellen" + echo "5) Media-Dateien wiederherstellen" + echo "6) Staticfiles wiederherstellen" + echo "7) Docker-Image importieren" + echo "8) Konfigurationsdatei wiederherstellen" + echo "q) Beenden" + echo + read -p "Bitte Auswahl eingeben: " auswahl + + case $auswahl in + 1) + /home/thomas/scripte/backup_docker.sh + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && continue + ;; + 2) + read -p "Backup-Zeitstempel (z für zurück): " ts + [[ "$ts" =~ ^[zZ]$ ]] && continue + /home/thomas/scripte/restore_docker.sh "$ts" + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && continue + ;; + 3) + while true; do + clear + echo "--- Einzelne Bereiche sichern ---" + echo "a) Einzelne Datenbank sichern" + echo "b) Media sichern" + echo "c) Staticfiles sichern" + echo "d) Docker-Image sichern" + echo "e) Konfigurationsdatei sichern" + echo "z) Zurück" + echo + read -p "Bitte Auswahl eingeben: " sub + case $sub in + a) + read -p "Containername (z für zurück): " ctn + [[ "$ctn" =~ ^[zZ]$ ]] && break + /home/thomas/scripte/Einzelne_Restore_Scripte/backup_db.sh "$ctn" + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && break + ;; + b) + read -p "Projekt (webshop|ssv, z für zurück): " prj + [[ "$prj" =~ ^[zZ]$ ]] && break + /home/thomas/scripte/Einzelne_Restore_Scripte/backup_media.sh "$prj" + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && break + ;; + c) + read -p "Projekt (webshop|ssv, z für zurück): " prj + [[ "$prj" =~ ^[zZ]$ ]] && break + /home/thomas/scripte/Einzelne_Restore_Scripte/backup_staticfiles.sh "$prj" + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && break + ;; + d) + read -p "Image-Name (z.B. nginx:latest, z für zurück): " img + [[ "$img" =~ ^[zZ]$ ]] && break + /home/thomas/scripte/Einzelne_Restore_Scripte/backup_image.sh "$img" + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && break + ;; + e) + read -p "Datei (docker-compose.yml|nginx.conf, z für zurück): " datei + [[ "$datei" =~ ^[zZ]$ ]] && break + /home/thomas/scripte/Einzelne_Restore_Scripte/backup_config.sh "$datei" + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && break + ;; + z|Z) + break + ;; + *) + echo "Ungültige Auswahl." + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && break + ;; + esac + done + ;; + 4) + read -p "Backup-Zeitstempel (z für zurück): " ts + [[ "$ts" =~ ^[zZ]$ ]] && continue + read -p "Containername (z.B. db_webshop, z für zurück): " ctn + [[ "$ctn" =~ ^[zZ]$ ]] && continue + /home/thomas/scripte/Einzelne_Restore_Scripte/restore_db.sh "$ts" "$ctn" + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && continue + ;; + 5) + read -p "Backup-Zeitstempel (z für zurück): " ts + [[ "$ts" =~ ^[zZ]$ ]] && continue + read -p "Projekt (webshop|ssv, z für zurück): " prj + [[ "$prj" =~ ^[zZ]$ ]] && continue + /home/thomas/scripte/Einzelne_Restore_Scripte/restore_media.sh "$ts" "$prj" + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && continue + ;; + 6) + read -p "Backup-Zeitstempel (z für zurück): " ts + [[ "$ts" =~ ^[zZ]$ ]] && continue + read -p "Projekt (webshop|ssv, z für zurück): " prj + [[ "$prj" =~ ^[zZ]$ ]] && continue + /home/thomas/scripte/Einzelne_Restore_Scripte/restore_staticfiles.sh "$ts" "$prj" + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && continue + ;; + 7) + read -p "Backup-Zeitstempel (z für zurück): " ts + [[ "$ts" =~ ^[zZ]$ ]] && continue + read -p "Image-Name (z.B. nginx_latest, z für zurück): " img + [[ "$img" =~ ^[zZ]$ ]] && continue + /home/thomas/scripte/Einzelne_Restore_Scripte/restore_image.sh "$ts" "$img" + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && continue + ;; + 8) + read -p "Backup-Zeitstempel (z für zurück): " ts + [[ "$ts" =~ ^[zZ]$ ]] && continue + read -p "Datei (docker-compose.yml|nginx.conf, z für zurück): " datei + [[ "$datei" =~ ^[zZ]$ ]] && continue + /home/thomas/scripte/Einzelne_Restore_Scripte/restore_config.sh "$ts" "$datei" + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && continue + ;; + q) + echo "Beende Menü." + exit 0 + ;; + *) + echo "Ungültige Auswahl." + read -p "Weiter mit Enter... (z für zurück)" z; [[ "$z" =~ ^[zZ]$ ]] && continue + ;; + esac +done \ No newline at end of file