88 lines
3.0 KiB
Bash
Executable File
88 lines
3.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
CREDS_FILE="${CREDS_FILE:-$HOME/.openclaw/credentials/minio-zap.env}"
|
|
MC_BIN="${MC_BIN:-$HOME/.openclaw/workspace/bin/mc}"
|
|
PREFIX_ROOT="${PREFIX_ROOT:-workspace-backups}"
|
|
MAX_AGE_HOURS="${MAX_AGE_HOURS:-12}"
|
|
|
|
if [[ ! -x "$MC_BIN" ]]; then
|
|
MC_BIN="$(command -v mc || true)"
|
|
fi
|
|
|
|
fail() {
|
|
echo "STATE=FAIL"
|
|
echo "ERROR=$1"
|
|
exit 1
|
|
}
|
|
|
|
[[ -f "$CREDS_FILE" ]] || fail "Missing creds file: $CREDS_FILE"
|
|
[[ -x "$MC_BIN" ]] || fail "MinIO client not found (set MC_BIN)"
|
|
|
|
# shellcheck disable=SC1090
|
|
source "$CREDS_FILE"
|
|
|
|
[[ -n "${MINIO_ENDPOINT:-}" ]] || fail "MINIO_ENDPOINT missing"
|
|
[[ -n "${MINIO_ACCESS_KEY:-}" ]] || fail "MINIO_ACCESS_KEY missing"
|
|
[[ -n "${MINIO_SECRET_KEY:-}" ]] || fail "MINIO_SECRET_KEY missing"
|
|
[[ -n "${MINIO_BUCKET:-}" ]] || fail "MINIO_BUCKET missing"
|
|
|
|
"$MC_BIN" alias set minio "$MINIO_ENDPOINT" "$MINIO_ACCESS_KEY" "$MINIO_SECRET_KEY" >/dev/null
|
|
|
|
latest_prefix="$($MC_BIN ls "minio/$MINIO_BUCKET/$PREFIX_ROOT" 2>/dev/null | awk '{print $NF}' | tr -d '/' | grep -E '^[0-9]{8}T[0-9]{6}Z$' | sort | tail -n1 || true)"
|
|
[[ -n "$latest_prefix" ]] || fail "No backup prefixes found under $PREFIX_ROOT"
|
|
|
|
# freshness check
|
|
backup_iso="${latest_prefix:0:4}-${latest_prefix:4:2}-${latest_prefix:6:2} ${latest_prefix:9:2}:${latest_prefix:11:2}:${latest_prefix:13:2} UTC"
|
|
backup_epoch="$(date -u -d "$backup_iso" +%s 2>/dev/null || echo 0)"
|
|
now_epoch="$(date -u +%s)"
|
|
[[ "$backup_epoch" -gt 0 ]] || fail "Could not parse timestamp from prefix: $latest_prefix"
|
|
age_h=$(( (now_epoch - backup_epoch) / 3600 ))
|
|
if (( age_h > MAX_AGE_HOURS )); then
|
|
fail "Latest backup too old (${age_h}h > ${MAX_AGE_HOURS}h): $latest_prefix"
|
|
fi
|
|
|
|
prefix_path="minio/$MINIO_BUCKET/$PREFIX_ROOT/$latest_prefix"
|
|
archive="openclaw-${latest_prefix}.tar.gz"
|
|
sha_file="${archive}.sha256"
|
|
manifest="manifest.txt"
|
|
|
|
# Ensure required objects exist
|
|
"$MC_BIN" stat "$prefix_path/$archive" >/dev/null 2>&1 || fail "Missing archive: $archive"
|
|
"$MC_BIN" stat "$prefix_path/$sha_file" >/dev/null 2>&1 || fail "Missing checksum: $sha_file"
|
|
"$MC_BIN" stat "$prefix_path/$manifest" >/dev/null 2>&1 || fail "Missing manifest: $manifest"
|
|
|
|
TMPDIR="$(mktemp -d)"
|
|
trap 'rm -rf "$TMPDIR"' EXIT
|
|
|
|
"$MC_BIN" cp "$prefix_path/$archive" "$TMPDIR/" >/dev/null
|
|
"$MC_BIN" cp "$prefix_path/$sha_file" "$TMPDIR/" >/dev/null
|
|
"$MC_BIN" cp "$prefix_path/$manifest" "$TMPDIR/" >/dev/null
|
|
|
|
cd "$TMPDIR"
|
|
awk '{print $1" '$archive'"}' "$sha_file" > check.sha256
|
|
sha256sum -c check.sha256 >/dev/null || fail "SHA256 verification failed"
|
|
|
|
mkdir -p restore
|
|
# Extract without touching live ~/.openclaw
|
|
tar -xzf "$archive" -C restore || fail "Archive extraction failed"
|
|
|
|
# Basic structure checks
|
|
for req in \
|
|
"restore/.openclaw/openclaw.json" \
|
|
"restore/.openclaw/agents" \
|
|
"restore/.openclaw/credentials" \
|
|
"restore/.openclaw/workspace"; do
|
|
[[ -e "$req" ]] || fail "Missing required path in restore: $req"
|
|
done
|
|
|
|
size_bytes="$(stat -c '%s' "$archive")"
|
|
|
|
echo "STATE=PASS"
|
|
echo "LATEST_PREFIX=$latest_prefix"
|
|
echo "AGE_HOURS=$age_h"
|
|
echo "ARCHIVE_BYTES=$size_bytes"
|
|
echo "CHECKSUM=OK"
|
|
echo "EXTRACT=OK"
|
|
echo "STRUCTURE=OK"
|