Explorer
/root/verify-restic-backups.sh
← Zurück ↓ Download
#!/usr/bin/env bash

set -u

REPO="/mnt/mybooklive-vps/RESTIC"
PASS="/etc/restic/nas-password"
BASE="/tmp/restic-verify"
REPORT="/root/restic-verification-2026-08-13.txt"

rm -rf "$BASE"
mkdir -p "$BASE"
: > "$REPORT"

exec > >(tee -a "$REPORT") 2>&1

echo "============================================================"
echo " RESTIC GESAMTPRÜFUNG"
echo " Start: $(date -Is)"
echo "============================================================"
echo

FAIL=0
WARN=0

ok()   { echo "[OK]   $*"; }
warn() { echo "[WARN] $*"; WARN=$((WARN+1)); }
fail() { echo "[FAIL] $*"; FAIL=$((FAIL+1)); }

echo "=== 1. SNAPSHOT-LISTE ==="
restic -r "$REPO" --password-file "$PASS" snapshots || fail "Snapshot-Liste fehlgeschlagen"
echo

echo "=== 2. RESTIC REPOSITORY CHECK --read-data ==="
if restic -r "$REPO" --password-file "$PASS" check --read-data; then
    ok "Restic Repository inklusive gespeicherter Datenblöcke"
else
    fail "Restic Repository-Check"
fi
echo

declare -A SNAPS=(
  [obsidian]="cbca7e22"
  [cron]="bba732f7"
  [kanban]="34aaf8c8"
  [sma]="13c7bb0e"
  [n8n]="4d052153"
  [graphiti]="8f1d4f17"
  [mirofish]="95a57838"
  [couchdb]="43070023"
  [vento]="56ca305d"
  [lead-engine]="aaea505e"
  [hermes-skills]="11fb7f73"
  [hermes-prompts-scripts]="4c2c3db3"
  [hermes-workstates]="6f84f8e7"
  [knowledge-databases]="e017a3f4"
  [project-core]="dc37c090"
  [courses]="eb5d2561"
  [social-media]="e35f24e5"
  [research-apps]="abb6bfb9"
  [reports-reference]="3b95c8b7"
  [worker-watcher]="a6ed70a1"
  [solar-wirtschaftlichkeit]="42a2b280"
  [telefon-agent]="b054da41"
  [the-donna]="feff007a"
  [vps-admin-reference]="45100dc8"
)

echo "=== 3. ALLE SNAPSHOTS RESTAURIEREN ==="

for name in "${!SNAPS[@]}"; do
    snap="${SNAPS[$name]}"
    target="$BASE/$name"

    mkdir -p "$target"

    echo
    echo "--- $name / $snap ---"

    if restic -r "$REPO" --password-file "$PASS" \
        restore "$snap" --target "$target"; then

        files=$(find "$target" -type f | wc -l)
        bytes=$(du -sb "$target" 2>/dev/null | awk '{print $1}')

        if [ "$files" -gt 0 ]; then
            ok "$name restauriert: $files Dateien, $bytes Bytes"
        else
            fail "$name restauriert, aber keine Dateien gefunden"
        fi
    else
        fail "$name Restore fehlgeschlagen"
    fi
done

echo
echo "=== 4. SQLITE-INTEGRITÄT ==="

SQLITE_COUNT=0
SQLITE_FAIL=0

while IFS= read -r -d '' f; do
    if head -c 16 "$f" 2>/dev/null | grep -aq "SQLite format 3"; then
        SQLITE_COUNT=$((SQLITE_COUNT+1))
        result=$(sqlite3 "$f" "PRAGMA integrity_check;" 2>&1)

        if [ "$result" = "ok" ]; then
            ok "SQLite: $f"
        else
            fail "SQLite: $f -> $result"
            SQLITE_FAIL=$((SQLITE_FAIL+1))
        fi
    fi
done < <(
    find "$BASE" -type f \
      \( -name '*.db' -o -name '*.sqlite' -o -name '*.sqlite3' \) \
      -print0
)

echo
echo "SQLite-Datenbanken erkannt: $SQLITE_COUNT"
echo "SQLite-Fehler:              $SQLITE_FAIL"

echo
echo "=== 5. JSON-EXPORTS / REFERENZDATEIEN ==="

JSON_COUNT=0
JSON_FAIL=0

while IFS= read -r -d '' f; do
    JSON_COUNT=$((JSON_COUNT+1))
    if python3 -m json.tool "$f" >/dev/null 2>&1; then
        :
    else
        # JSONL ist absichtlich kein einzelnes JSON-Dokument.
        case "$f" in
            *.jsonl)
                ;;
            *)
                warn "JSON nicht parsebar: $f"
                JSON_FAIL=$((JSON_FAIL+1))
                ;;
        esac
    fi
done < <(find "$BASE" -type f \( -name '*.json' -o -name '*.jsonl' \) -print0)

echo "JSON/JSONL-Dateien geprüft: $JSON_COUNT"
echo "JSON-Warnungen:             $JSON_FAIL"

echo
echo "=== 6. NEO4J-DUMPS ==="

GRAPH_DUMP=$(find "$BASE/graphiti" -type f -name 'neo4j.dump' -print -quit 2>/dev/null || true)
MIRO_DUMP=$(find "$BASE/mirofish" -type f -name 'neo4j.dump' -print -quit 2>/dev/null || true)

check_dump() {
    local label="$1"
    local image="$2"
    local dump="$3"

    if [ -z "$dump" ] || [ ! -s "$dump" ]; then
        fail "$label Neo4j-Dump fehlt oder ist leer"
        return
    fi

    local dir
    dir=$(dirname "$dump")

    echo "--- $label ---"
    ls -lh "$dump"

    if docker run --rm \
        -v "$dir:/backup:ro" \
        "$image" \
        neo4j-admin database load neo4j \
        --from-path=/backup \
        --info; then
        ok "$label Neo4j-Dump wird von neo4j-admin erkannt"
    else
        warn "$label Dump konnte mit load --info nicht validiert werden"
    fi
}

check_dump "Graphiti" "neo4j:5.26-community" "$GRAPH_DUMP"
check_dump "MiroFish" "neo4j:5.18-community" "$MIRO_DUMP"

echo
echo "=== 7. SPEZIELLE PFLICHTDATEIEN ==="

require_pattern() {
    local label="$1"
    local root="$2"
    local pattern="$3"

    if find "$root" -type f -name "$pattern" -print -quit 2>/dev/null | grep -q .; then
        ok "$label vorhanden"
    else
        fail "$label fehlt"
    fi
}

require_pattern "Cron jobs.json" "$BASE/cron" "jobs.json"
require_pattern "Kanban kanban.db" "$BASE/kanban" "kanban.db"
require_pattern "SMA signals.db" "$BASE/sma" "signals.db"
require_pattern "n8n database.sqlite" "$BASE/n8n" "database.sqlite"
require_pattern "YouTube Knowledge DB" "$BASE/knowledge-databases" "youtube-research-knowledge.db"
require_pattern "Knowledge Pipeline DB" "$BASE/knowledge-databases" "knowledge-pipeline.db"
require_pattern "Worker Watcher DB" "$BASE/worker-watcher" "watcher.sqlite"
require_pattern "Solar DB" "$BASE/solar-wirtschaftlichkeit" "solar.sqlite"

echo
echo "=== 8. GESAMTERGEBNIS ==="
echo "Ende: $(date -Is)"
echo
echo "FEHLER:   $FAIL"
echo "WARNUNGEN:$WARN"

if [ "$FAIL" -eq 0 ]; then
    echo
    echo "GESAMTSTATUS: OK"
else
    echo
    echo "GESAMTSTATUS: FEHLER"
fi

echo
echo "Bericht: $REPORT"
echo "Restore-Verzeichnis: $BASE"
echo "============================================================"

exit 0