mirror of
https://github.com/acedanger/shell.git
synced 2025-12-05 21:40:12 -08:00
feat: Enhance Jellyseerr backup functionality with specialized methods and verification
This commit is contained in:
223
backup-media.sh
223
backup-media.sh
@@ -124,8 +124,8 @@ declare -A MEDIA_SERVICES=(
|
||||
["audiobookshelf"]="/metadata/backups"
|
||||
["tautulli"]="/config/backups"
|
||||
["sabnzbd"]="/config/sabnzbd.ini"
|
||||
["jellyseerr_db"]="/config/db/"
|
||||
["jellyseerr_settings"]="/data/settings.json"
|
||||
["jellyseerr_db"]="/config/db/db.sqlite3"
|
||||
["jellyseerr_settings"]="/config/settings.json"
|
||||
)
|
||||
|
||||
# Service-specific backup destinations
|
||||
@@ -136,8 +136,8 @@ declare -A BACKUP_DESTINATIONS=(
|
||||
["audiobookshelf"]="${BACKUP_ROOT}/audiobookshelf/"
|
||||
["tautulli"]="${BACKUP_ROOT}/tautulli/"
|
||||
["sabnzbd"]="${BACKUP_ROOT}/sabnzbd/sabnzbd_$(date +%Y%m%d).ini"
|
||||
["jellyseerr_db"]="${BACKUP_ROOT}/jellyseerr/backup_$(date +%Y%m%d)/"
|
||||
["jellyseerr_settings"]="${BACKUP_ROOT}/jellyseerr/backup_$(date +%Y%m%d)/"
|
||||
["jellyseerr_db"]="${BACKUP_ROOT}/jellyseerr/jellyseerr_db_$(date +%Y%m%d_%H%M%S).sqlite3"
|
||||
["jellyseerr_settings"]="${BACKUP_ROOT}/jellyseerr/settings_$(date +%Y%m%d_%H%M%S).json"
|
||||
)
|
||||
|
||||
# Logging functions
|
||||
@@ -455,6 +455,11 @@ backup_service() {
|
||||
|
||||
log_message "Starting backup for service: $service"
|
||||
|
||||
# Handle Jellyseerr services with specialized backup function
|
||||
if [[ "$service" == jellyseerr_* ]]; then
|
||||
return $(backup_jellyseerr_service "$service")
|
||||
fi
|
||||
|
||||
# Handle special cases for container names
|
||||
case "$service" in
|
||||
jellyseerr_db|jellyseerr_settings)
|
||||
@@ -471,11 +476,6 @@ backup_service() {
|
||||
local src_path="${MEDIA_SERVICES[$service]}"
|
||||
local dest_path="${BACKUP_DESTINATIONS[$service]}"
|
||||
|
||||
# Create destination directory for jellyseerr
|
||||
if [[ "$service" == jellyseerr_* ]]; then
|
||||
mkdir -p "$(dirname "$dest_path")"
|
||||
fi
|
||||
|
||||
# Perform the backup
|
||||
if [ "$DRY_RUN" == true ]; then
|
||||
log_info "DRY RUN: Would backup $container:$src_path to $dest_path"
|
||||
@@ -551,19 +551,210 @@ backup_service() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Backup service wrapper for parallel execution
|
||||
backup_service_wrapper() {
|
||||
# Specialized Jellyseerr backup function using SQLite CLI method
|
||||
backup_jellyseerr_service() {
|
||||
local service="$1"
|
||||
local temp_file="$2"
|
||||
local container="jellyseerr"
|
||||
local backup_start_time
|
||||
backup_start_time=$(date +%s)
|
||||
|
||||
if backup_service "$service"; then
|
||||
echo "SUCCESS:$service" >> "$temp_file"
|
||||
log_message "Starting specialized Jellyseerr backup for: $service"
|
||||
|
||||
# Check if container is running
|
||||
if ! check_container_running "$container"; then
|
||||
log_file_details "$service" "${container}:${MEDIA_SERVICES[$service]}" "${BACKUP_DESTINATIONS[$service]}" "FAILED - Container not running"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local src_path="${MEDIA_SERVICES[$service]}"
|
||||
local dest_path="${BACKUP_DESTINATIONS[$service]}"
|
||||
|
||||
# Create destination directory
|
||||
mkdir -p "$(dirname "$dest_path")"
|
||||
|
||||
# Perform the backup
|
||||
if [ "$DRY_RUN" == true ]; then
|
||||
log_info "DRY RUN: Would backup $container:$src_path to $dest_path"
|
||||
log_file_details "$service" "$container:$src_path" "$dest_path" "DRY RUN"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$INTERACTIVE_MODE" == true ]; then
|
||||
echo -n "Backup $service? (y/N): "
|
||||
read -r response
|
||||
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||
log_info "Skipping $service backup (user choice)"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Handle different backup methods based on service type
|
||||
local backup_success=false
|
||||
|
||||
if [[ "$service" == "jellyseerr_db" ]]; then
|
||||
# Use SQLite CLI method for database backup (recommended by Jellyseerr docs)
|
||||
log_info "Using SQLite CLI method for database backup"
|
||||
|
||||
# Create a temporary backup file path inside the container
|
||||
local temp_backup="/tmp/jellyseerr_db_$(date +%Y%m%d_%H%M%S).sqlite3"
|
||||
|
||||
# Execute SQLite backup command inside the container
|
||||
local sqlite_cmd="docker exec $container sqlite3 $src_path \".backup '$temp_backup'\""
|
||||
log_info "Executing: $sqlite_cmd"
|
||||
|
||||
if eval "$sqlite_cmd" 2>&1 | tee -a "$LOG_FILE"; then
|
||||
# Copy the backup file from container to host
|
||||
local copy_cmd="docker cp $container:$temp_backup $dest_path"
|
||||
log_info "Executing: $copy_cmd"
|
||||
|
||||
if $copy_cmd 2>&1 | tee -a "$LOG_FILE"; then
|
||||
# Clean up temporary file in container
|
||||
docker exec "$container" rm -f "$temp_backup" 2>/dev/null || true
|
||||
backup_success=true
|
||||
else
|
||||
log_error "Failed to copy database backup from container"
|
||||
# Clean up temporary file in container
|
||||
docker exec "$container" rm -f "$temp_backup" 2>/dev/null || true
|
||||
fi
|
||||
else
|
||||
log_error "SQLite backup command failed"
|
||||
fi
|
||||
|
||||
elif [[ "$service" == "jellyseerr_settings" ]]; then
|
||||
# Standard file copy for settings
|
||||
local docker_cmd="docker cp $container:$src_path $dest_path"
|
||||
log_info "Executing: $docker_cmd"
|
||||
|
||||
if $docker_cmd 2>&1 | tee -a "$LOG_FILE"; then
|
||||
backup_success=true
|
||||
else
|
||||
log_error "Settings backup failed"
|
||||
fi
|
||||
else
|
||||
echo "FAILED:$service" >> "$temp_file"
|
||||
log_error "Unknown Jellyseerr service type: $service"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$backup_success" == true ]; then
|
||||
log_success "Backup completed for $service"
|
||||
|
||||
# File-level metrics tracking (success)
|
||||
if [[ "$METRICS_ENABLED" == "true" ]]; then
|
||||
local file_size checksum
|
||||
if [ -f "$dest_path" ]; then
|
||||
file_size=$(stat -c%s "$dest_path" 2>/dev/null || echo "0")
|
||||
checksum=$(md5sum "$dest_path" 2>/dev/null | cut -d' ' -f1 || echo "")
|
||||
metrics_add_file "$dest_path" "success" "$file_size" "$checksum"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify the backup
|
||||
if verify_jellyseerr_backup "$container" "$src_path" "$dest_path" "$service"; then
|
||||
log_file_details "$service" "$container:$src_path" "$dest_path" "SUCCESS"
|
||||
track_performance "backup_${service}" "$backup_start_time"
|
||||
return 0
|
||||
else
|
||||
log_file_details "$service" "$container:$src_path" "$dest_path" "VERIFICATION_FAILED"
|
||||
# File-level metrics tracking (verification failed)
|
||||
if [[ "$METRICS_ENABLED" == "true" ]]; then
|
||||
local file_size
|
||||
if [ -f "$dest_path" ]; then
|
||||
file_size=$(stat -c%s "$dest_path" 2>/dev/null || echo "0")
|
||||
metrics_add_file "$dest_path" "failed" "$file_size" "" "Verification failed"
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_error "Backup failed for $service"
|
||||
log_file_details "$service" "$container:$src_path" "$dest_path" "FAILED"
|
||||
# File-level metrics tracking (backup failed)
|
||||
if [[ "$METRICS_ENABLED" == "true" ]]; then
|
||||
local file_size
|
||||
if [ -f "$dest_path" ]; then
|
||||
file_size=$(stat -c%s "$dest_path" 2>/dev/null || echo "0")
|
||||
metrics_add_file "$dest_path" "failed" "$file_size" "" "Backup failed"
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Clean old backups based on age and count
|
||||
# Specialized verification for Jellyseerr backups
|
||||
verify_jellyseerr_backup() {
|
||||
local src_container="$1"
|
||||
local src_path="$2"
|
||||
local dest_path="$3"
|
||||
local service="$4"
|
||||
|
||||
if [ "$VERIFY_BACKUPS" != true ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Verifying Jellyseerr backup for $service"
|
||||
|
||||
# Check if backup file exists and has reasonable size
|
||||
if [ ! -f "$dest_path" ]; then
|
||||
log_error "Backup file not found: $dest_path"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local file_size
|
||||
file_size=$(stat -c%s "$dest_path" 2>/dev/null || echo "0")
|
||||
|
||||
if [ "$file_size" -eq 0 ]; then
|
||||
log_error "Backup file is empty: $dest_path"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Service-specific verification
|
||||
if [[ "$service" == "jellyseerr_db" ]]; then
|
||||
# For SQLite database, verify it's a valid SQLite file
|
||||
if command -v file >/dev/null 2>&1; then
|
||||
if file "$dest_path" | grep -q "SQLite"; then
|
||||
log_success "Database backup verification passed: Valid SQLite file ($file_size bytes)"
|
||||
return 0
|
||||
else
|
||||
log_error "Database backup verification failed: Not a valid SQLite file"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
# Fallback: just check file size is reasonable (>1KB for a database)
|
||||
if [ "$file_size" -gt 1024 ]; then
|
||||
log_success "Database backup verification passed: File size check ($file_size bytes)"
|
||||
return 0
|
||||
else
|
||||
log_error "Database backup verification failed: File too small ($file_size bytes)"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
elif [[ "$service" == "jellyseerr_settings" ]]; then
|
||||
# For settings JSON, verify it's valid JSON
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
if jq empty "$dest_path" 2>/dev/null; then
|
||||
log_success "Settings backup verification passed: Valid JSON file ($file_size bytes)"
|
||||
return 0
|
||||
else
|
||||
log_error "Settings backup verification failed: Invalid JSON file"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
# Fallback: check if it looks like JSON (starts with { or [)
|
||||
if head -c 1 "$dest_path" | grep -q "[{\[]"; then
|
||||
log_success "Settings backup verification passed: JSON format check ($file_size bytes)"
|
||||
return 0
|
||||
else
|
||||
log_error "Settings backup verification failed: Does not appear to be JSON"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
log_warning "Unable to verify backup for $service"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Clean up old backups based on age and count
|
||||
cleanup_old_backups() {
|
||||
log_message "Cleaning up old backups..."
|
||||
|
||||
|
||||
Reference in New Issue
Block a user