#!/bin/bash # WEVIA Snapshot Archiver v2 — Bash (robust, no Python deps) # Usage: bash wevia-snap-archiver.sh # set -e SNAP_ID="$1" TAG="$2" REL_NAME="$3" HZ="xUcbvWMjkMgetuTU0llazUgB85jc7aQBLMhQ79NZ1Yf7j2TRF598DfNxoVrMnVOj" GH="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" LOG="/tmp/snap-$TAG.log" log() { echo "[$(date +%H:%M:%S)] $1" | tee -a "$LOG"; } log "=== ARCHIVING SNAPSHOT $SNAP_ID → $TAG ===" # 1. Create temp server log "Creating temp server..." RESULT=$(curl -s -X POST "https://api.hetzner.cloud/v1/servers" \ -H "Authorization: Bearer $HZ" -H "Content-Type: application/json" \ -d "{\"name\":\"temp-$TAG\",\"server_type\":\"ccx23\",\"image\":$SNAP_ID,\"location\":\"hel1\",\"start_after_create\":true,\"ssh_keys\":[109134687],\"networks\":[12033255]}") SRV_ID=$(echo "$RESULT" | python3 -c "import sys,json;print(json.load(sys.stdin)['server']['id'])" 2>/dev/null) PUB_IP=$(echo "$RESULT" | python3 -c "import sys,json;print(json.load(sys.stdin)['server']['public_net']['ipv4']['ip'])" 2>/dev/null) log "Server $SRV_ID IP=$PUB_IP" # 2. Wait for creation (max 20 min) log "Waiting for server creation..." for i in $(seq 1 60); do sleep 20 STATUS=$(curl -s "https://api.hetzner.cloud/v1/servers/$SRV_ID" -H "Authorization: Bearer $HZ" | python3 -c "import sys,json;print(json.load(sys.stdin)['server']['status'])" 2>/dev/null) PROGRESS=$(curl -s "https://api.hetzner.cloud/v1/servers/$SRV_ID/actions?per_page=3" -H "Authorization: Bearer $HZ" | python3 -c "import sys,json;acts=json.load(sys.stdin)['actions'];print(all(a['status']=='success' for a in acts))" 2>/dev/null) log " [$i] status=$STATUS done=$PROGRESS" [ "$PROGRESS" = "True" ] && break done # 3. Rescue mode log "Poweroff..." curl -s -X POST "https://api.hetzner.cloud/v1/servers/$SRV_ID/actions/poweroff" -H "Authorization: Bearer $HZ" > /dev/null sleep 25 log "Enable rescue..." RESCUE_PW=$(curl -s -X POST "https://api.hetzner.cloud/v1/servers/$SRV_ID/actions/enable_rescue" \ -H "Authorization: Bearer $HZ" -H "Content-Type: application/json" \ -d '{"type":"linux64","ssh_keys":[109134687]}' | python3 -c "import sys,json;print(json.load(sys.stdin)['root_password'])" 2>/dev/null) log "Rescue PW: $RESCUE_PW" sleep 10 log "Poweron..." curl -s -X POST "https://api.hetzner.cloud/v1/servers/$SRV_ID/actions/poweron" -H "Authorization: Bearer $HZ" > /dev/null sleep 90 # 4. SSH to rescue log "Connecting to rescue..." CONNECTED=$(sshpass -p "$RESCUE_PW" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=20 root@$PUB_IP 'echo OK' 2>/dev/null) if [ "$CONNECTED" != "OK" ]; then log "RESCUE SSH FAILED — retrying in 60s..." sleep 60 CONNECTED=$(sshpass -p "$RESCUE_PW" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=20 root@$PUB_IP 'echo OK' 2>/dev/null) fi if [ "$CONNECTED" != "OK" ]; then log "FATAL: Cannot connect to rescue. Cleaning up." curl -s -X DELETE "https://api.hetzner.cloud/v1/servers/$SRV_ID" -H "Authorization: Bearer $HZ" > /dev/null exit 1 fi log "CONNECTED! Mounting disk..." sshpass -p "$RESCUE_PW" ssh -o StrictHostKeyChecking=no root@$PUB_IP 'mkdir -p /mnt/data && mount /dev/sda1 /mnt/data' 2>/dev/null # 5. Scan + archive log "Scanning..." CONTENT=$(sshpass -p "$RESCUE_PW" ssh -o StrictHostKeyChecking=no root@$PUB_IP 'du -sh /mnt/data/opt/* /mnt/data/var/www/* 2>/dev/null | sort -rh | head -20' 2>/dev/null) log "$CONTENT" VAULT="/opt/wevads/vault/$TAG" mkdir -p "$VAULT" for ARCH in "opt-all:opt/" "www-all:var/www/" "configs:var/spool/cron/ etc/ssh/ root/" "postgresql:var/lib/postgresql/"; do NAME="${ARCH%%:*}" PATHS="${ARCH#*:}" log "Creating $NAME..." sshpass -p "$RESCUE_PW" ssh -o StrictHostKeyChecking=no root@$PUB_IP "cd /mnt/data && tar czf /tmp/$NAME.tar.gz --exclude='node_modules' --exclude='.git' --exclude='vendor' --exclude='cache' --exclude='.cache' --exclude='.cursor-server' $PATHS 2>/dev/null; ls -lh /tmp/$NAME.tar.gz" 2>/dev/null log "SCP $NAME to S204..." sshpass -p "$RESCUE_PW" scp -o StrictHostKeyChecking=no "root@$PUB_IP:/tmp/$NAME.tar.gz" "$VAULT/" 2>/dev/null && log "$NAME OK" || log "$NAME FAILED" done log "Archives: $(ls -lh $VAULT/)" # 6. GitHub release + upload log "Creating GitHub release $TAG..." REL_ID=$(curl -s -X POST "https://api.github.com/repos/Yacineutt/weval-archive/releases" \ -H "Authorization: token $GH" -H "Content-Type: application/json" \ -d "{\"tag_name\":\"$TAG\",\"name\":\"$REL_NAME\",\"body\":\"Hetzner snapshot $SNAP_ID\"}" | python3 -c "import sys,json;print(json.load(sys.stdin).get('id',''))" 2>/dev/null) UPLOAD="https://uploads.github.com/repos/Yacineutt/weval-archive/releases/$REL_ID/assets" for f in $VAULT/*.tar.gz; do FNAME=$(basename "$f") log "Uploading $FNAME..." curl -s -H "Authorization: token $GH" -H "Content-Type: application/gzip" --data-binary @"$f" "$UPLOAD?name=$FNAME" > /dev/null 2>&1 log "$FNAME uploaded" done # 7. Cleanup log "Deleting temp server $SRV_ID..." curl -s -X DELETE "https://api.hetzner.cloud/v1/servers/$SRV_ID" -H "Authorization: Bearer $HZ" > /dev/null log "=== SNAPSHOT $SNAP_ID ARCHIVED → $TAG ==="