mirror of
https://github.com/acedanger/shell.git
synced 2025-12-06 00:00:13 -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"
|
["audiobookshelf"]="/metadata/backups"
|
||||||
["tautulli"]="/config/backups"
|
["tautulli"]="/config/backups"
|
||||||
["sabnzbd"]="/config/sabnzbd.ini"
|
["sabnzbd"]="/config/sabnzbd.ini"
|
||||||
["jellyseerr_db"]="/config/db/"
|
["jellyseerr_db"]="/config/db/db.sqlite3"
|
||||||
["jellyseerr_settings"]="/data/settings.json"
|
["jellyseerr_settings"]="/config/settings.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Service-specific backup destinations
|
# Service-specific backup destinations
|
||||||
@@ -136,8 +136,8 @@ declare -A BACKUP_DESTINATIONS=(
|
|||||||
["audiobookshelf"]="${BACKUP_ROOT}/audiobookshelf/"
|
["audiobookshelf"]="${BACKUP_ROOT}/audiobookshelf/"
|
||||||
["tautulli"]="${BACKUP_ROOT}/tautulli/"
|
["tautulli"]="${BACKUP_ROOT}/tautulli/"
|
||||||
["sabnzbd"]="${BACKUP_ROOT}/sabnzbd/sabnzbd_$(date +%Y%m%d).ini"
|
["sabnzbd"]="${BACKUP_ROOT}/sabnzbd/sabnzbd_$(date +%Y%m%d).ini"
|
||||||
["jellyseerr_db"]="${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/backup_$(date +%Y%m%d)/"
|
["jellyseerr_settings"]="${BACKUP_ROOT}/jellyseerr/settings_$(date +%Y%m%d_%H%M%S).json"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Logging functions
|
# Logging functions
|
||||||
@@ -455,6 +455,11 @@ backup_service() {
|
|||||||
|
|
||||||
log_message "Starting backup for service: $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
|
# Handle special cases for container names
|
||||||
case "$service" in
|
case "$service" in
|
||||||
jellyseerr_db|jellyseerr_settings)
|
jellyseerr_db|jellyseerr_settings)
|
||||||
@@ -471,11 +476,6 @@ backup_service() {
|
|||||||
local src_path="${MEDIA_SERVICES[$service]}"
|
local src_path="${MEDIA_SERVICES[$service]}"
|
||||||
local dest_path="${BACKUP_DESTINATIONS[$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
|
# Perform the backup
|
||||||
if [ "$DRY_RUN" == true ]; then
|
if [ "$DRY_RUN" == true ]; then
|
||||||
log_info "DRY RUN: Would backup $container:$src_path to $dest_path"
|
log_info "DRY RUN: Would backup $container:$src_path to $dest_path"
|
||||||
@@ -551,19 +551,210 @@ backup_service() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Backup service wrapper for parallel execution
|
# Specialized Jellyseerr backup function using SQLite CLI method
|
||||||
backup_service_wrapper() {
|
backup_jellyseerr_service() {
|
||||||
local service="$1"
|
local service="$1"
|
||||||
local temp_file="$2"
|
local container="jellyseerr"
|
||||||
|
local backup_start_time
|
||||||
|
backup_start_time=$(date +%s)
|
||||||
|
|
||||||
if backup_service "$service"; then
|
log_message "Starting specialized Jellyseerr backup for: $service"
|
||||||
echo "SUCCESS:$service" >> "$temp_file"
|
|
||||||
|
# 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
|
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
|
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() {
|
cleanup_old_backups() {
|
||||||
log_message "Cleaning up old backups..."
|
log_message "Cleaning up old backups..."
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user