#!/bin/bash # # Gondulf SQLite Database Restore Script # Compatible with both Podman and Docker (auto-detects) # # Usage: ./restore.sh # # CAUTION: This will REPLACE the current database! # A safety backup will be created before restoration. # set -euo pipefail # Auto-detect container engine detect_container_engine() { if [ -n "${CONTAINER_ENGINE:-}" ]; then echo "$CONTAINER_ENGINE" elif command -v podman &> /dev/null; then echo "podman" elif command -v docker &> /dev/null; then echo "docker" else echo "ERROR: Neither podman nor docker found" >&2 exit 1 fi } # Check arguments if [ $# -ne 1 ]; then echo "Usage: $0 " echo "" echo "Example:" echo " $0 ./backups/gondulf_backup_20251120_120000.db.gz" echo " $0 ./backups/gondulf_backup_20251120_120000.db" echo "" exit 1 fi BACKUP_FILE="$1" ENGINE=$(detect_container_engine) CONTAINER_NAME="${CONTAINER_NAME:-gondulf}" echo "=========================================" echo "Gondulf Database Restore" echo "=========================================" echo "Container engine: $ENGINE" echo "Container name: $CONTAINER_NAME" echo "Backup file: $BACKUP_FILE" echo "" echo "⚠️ WARNING: This will REPLACE the current database!" echo "" # Validate backup file exists if [ ! -f "$BACKUP_FILE" ]; then echo "ERROR: Backup file not found: $BACKUP_FILE" >&2 exit 1 fi # Configuration DATABASE_URL="${GONDULF_DATABASE_URL:-sqlite:////data/gondulf.db}" # Extract database path from URL if [[ "$DATABASE_URL" =~ ^sqlite:////(.+)$ ]]; then DB_PATH="/${BASH_REMATCH[1]}" elif [[ "$DATABASE_URL" =~ ^sqlite:///(.+)$ ]]; then DB_PATH="/data/${BASH_REMATCH[1]}" else echo "ERROR: Invalid DATABASE_URL format: $DATABASE_URL" >&2 exit 1 fi echo "Database path in container: $DB_PATH" # Check if container is running CONTAINER_RUNNING=false if $ENGINE ps | grep -q "$CONTAINER_NAME"; then CONTAINER_RUNNING=true echo "Container status: running" echo "" echo "⚠️ Container is running. It will be stopped during restoration." read -p "Continue? [y/N] " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "Restore cancelled." exit 0 fi echo "Stopping container..." $ENGINE stop "$CONTAINER_NAME" else echo "Container status: stopped" fi # Decompress if needed TEMP_FILE="" RESTORE_FILE="" if [[ "$BACKUP_FILE" == *.gz ]]; then echo "Decompressing backup..." TEMP_FILE=$(mktemp) gunzip -c "$BACKUP_FILE" > "$TEMP_FILE" RESTORE_FILE="$TEMP_FILE" echo "✓ Decompressed to temporary file" else RESTORE_FILE="$BACKUP_FILE" fi # Verify backup integrity before restore echo "Verifying backup integrity..." if ! sqlite3 "$RESTORE_FILE" "PRAGMA integrity_check;" | grep -q "ok"; then echo "ERROR: Backup integrity check failed" >&2 [ -n "$TEMP_FILE" ] && rm -f "$TEMP_FILE" exit 1 fi echo "✓ Backup integrity verified" # Create temporary container to access volume if container is stopped if [ "$CONTAINER_RUNNING" = false ]; then echo "Creating temporary container to access volume..." TEMP_CONTAINER="${CONTAINER_NAME}_restore_temp" $ENGINE run -d --name "$TEMP_CONTAINER" \ -v gondulf_data:/data \ alpine:latest sleep 300 CONTAINER_NAME="$TEMP_CONTAINER" fi # Create safety backup of current database echo "Creating safety backup of current database..." SAFETY_BACKUP_CONTAINER="/data/gondulf_pre_restore_$(date +%Y%m%d_%H%M%S).db" if $ENGINE exec "$CONTAINER_NAME" test -f "$DB_PATH" 2>/dev/null; then $ENGINE exec "$CONTAINER_NAME" cp "$DB_PATH" "$SAFETY_BACKUP_CONTAINER" || { echo "WARNING: Failed to create safety backup" >&2 } echo "✓ Safety backup created: $SAFETY_BACKUP_CONTAINER" else echo " No existing database found (first time setup)" fi # Copy restore file into container RESTORE_FILE_CONTAINER="/tmp/restore_db.tmp" echo "Copying backup to container..." $ENGINE cp "$RESTORE_FILE" "$CONTAINER_NAME:$RESTORE_FILE_CONTAINER" # Perform restore echo "Restoring database..." $ENGINE exec "$CONTAINER_NAME" sh -c "cp '$RESTORE_FILE_CONTAINER' '$DB_PATH'" # Verify restored database echo "Verifying restored database..." if $ENGINE exec "$CONTAINER_NAME" sqlite3 "$DB_PATH" "PRAGMA integrity_check;" | grep -q "ok"; then echo "✓ Restored database integrity verified" else echo "ERROR: Restored database integrity check failed" >&2 echo "Attempting to restore from safety backup..." if $ENGINE exec "$CONTAINER_NAME" test -f "$SAFETY_BACKUP_CONTAINER" 2>/dev/null; then $ENGINE exec "$CONTAINER_NAME" cp "$SAFETY_BACKUP_CONTAINER" "$DB_PATH" echo "✓ Reverted to safety backup" fi # Clean up $ENGINE exec "$CONTAINER_NAME" rm -f "$RESTORE_FILE_CONTAINER" [ -n "$TEMP_FILE" ] && rm -f "$TEMP_FILE" # Stop temporary container if created if [ "$CONTAINER_RUNNING" = false ]; then $ENGINE stop "$TEMP_CONTAINER" 2>/dev/null || true $ENGINE rm "$TEMP_CONTAINER" 2>/dev/null || true fi exit 1 fi # Clean up temporary restore file in container $ENGINE exec "$CONTAINER_NAME" rm -f "$RESTORE_FILE_CONTAINER" # Clean up temporary decompressed file on host [ -n "$TEMP_FILE" ] && rm -f "$TEMP_FILE" # Stop and remove temporary container if we created one if [ "$CONTAINER_RUNNING" = false ]; then echo "Cleaning up temporary container..." $ENGINE stop "$TEMP_CONTAINER" 2>/dev/null || true $ENGINE rm "$TEMP_CONTAINER" 2>/dev/null || true CONTAINER_NAME="${CONTAINER_NAME%_restore_temp}" # Restore original name fi # Restart original container if it was running if [ "$CONTAINER_RUNNING" = true ]; then echo "Starting container..." $ENGINE start "$CONTAINER_NAME" echo "Waiting for container to be healthy..." sleep 5 fi echo "" echo "=========================================" echo "Restore complete!" echo "=========================================" echo "Backup restored from: $BACKUP_FILE" echo "Safety backup location: $SAFETY_BACKUP_CONTAINER" echo "" echo "Next steps:" echo "1. Verify the application is working correctly" echo "2. Once verified, you may delete the safety backup with:" echo " $ENGINE exec $CONTAINER_NAME rm $SAFETY_BACKUP_CONTAINER" echo ""