#!/usr/bin/env bash # gniza/lib/verify.sh — Remote backup integrity checks verify_account_backup() { local user="$1" local timestamp="${2:-}" local errors=0 # Resolve timestamp local ts; ts=$(resolve_snapshot_timestamp "$user" "$timestamp") || return 1 log_info "Verifying backup for $user (snapshot: $ts)..." if _is_rclone_mode; then local snap_subpath="accounts/${user}/snapshots/${ts}" # Check .complete marker if ! rclone_exists "${snap_subpath}/.complete"; then log_error "Snapshot missing .complete marker: $snap_subpath" return 1 fi # Count files local file_list; file_list=$(rclone_list_files "$snap_subpath" 2>/dev/null) || true local file_count=0 [[ -n "$file_list" ]] && file_count=$(echo "$file_list" | wc -l) if (( file_count == 0 )); then log_warn "No files found in snapshot" ((errors++)) || true else log_info " files: $file_count file(s)" fi # Check homedir if rclone_exists "${snap_subpath}/homedir/"; then local size_json; size_json=$(rclone_size "${snap_subpath}/homedir" 2>/dev/null) || true local bytes=0 if [[ -n "$size_json" ]]; then bytes=$(echo "$size_json" | grep -oP '"bytes":\s*\K[0-9]+' || echo 0) fi log_info " homedir: $(human_size "$bytes")" else log_warn "homedir directory missing in snapshot" fi # Check latest.txt local latest; latest=$(rclone_cat "accounts/${user}/snapshots/latest.txt" 2>/dev/null) || true if [[ -n "$latest" ]]; then log_info " latest -> $latest" else log_warn " latest.txt not set" fi else local snap_dir; snap_dir=$(get_snapshot_dir "$user") local snap_path="$snap_dir/$ts" # Check snapshot directory exists if ! remote_exec "test -d '$snap_path'" 2>/dev/null; then log_error "Snapshot directory not found: $snap_path" return 1 fi # Detect old format (pkgacct/ subdir) vs new format (content at root) local pkgacct_base="$snap_path" if remote_exec "test -d '$snap_path/pkgacct'" 2>/dev/null; then pkgacct_base="$snap_path/pkgacct" fi # Check for expected pkgacct files local file_count; file_count=$(remote_exec "find '$pkgacct_base' -maxdepth 1 -type f | wc -l" 2>/dev/null) if [[ "$file_count" -eq 0 ]]; then log_warn "No pkgacct files found in snapshot" ((errors++)) || true else log_info " pkgacct: $file_count file(s)" fi # Check for SQL files local sql_count; sql_count=$(remote_exec "find '$pkgacct_base/mysql' -name '*.sql.gz' 2>/dev/null | wc -l" 2>/dev/null) log_info " databases: $sql_count compressed SQL file(s)" # Check homedir directory if ! remote_exec "test -d '$snap_path/homedir'" 2>/dev/null; then log_warn "homedir directory missing in snapshot" else local homedir_size; homedir_size=$(remote_exec "du -sb '$snap_path/homedir' | cut -f1" 2>/dev/null) log_info " homedir: $(human_size "${homedir_size:-0}")" fi # Check latest symlink local base; base=$(get_remote_account_base "$user") local latest_target; latest_target=$(remote_exec "readlink '$base/latest' 2>/dev/null" 2>/dev/null) if [[ -n "$latest_target" ]]; then log_info " latest -> $(basename "$latest_target")" else log_warn " latest symlink not set" fi fi if (( errors > 0 )); then log_error "Verification found $errors issue(s) for $user" return 1 fi log_info "Verification passed for $user" return 0 } verify_all_accounts() { local accounts; accounts=$(list_remote_accounts) local total=0 passed=0 failed=0 if [[ -z "$accounts" ]]; then log_warn "No remote accounts found to verify" return 0 fi while IFS= read -r user; do [[ -z "$user" ]] && continue ((total++)) || true if verify_account_backup "$user"; then ((passed++)) || true else ((failed++)) || true fi done <<< "$accounts" echo "" log_info "Verification complete: $passed/$total passed, $failed failed" (( failed > 0 )) && return 1 return 0 }