#!/bin/bash # Laravel Timebank Backup Cleanup Script # Cleans up old backups based on retention policies and monitors disk usage # Usage: ./cleanup-backups.sh [options] # Options: --dry-run, --force, --verbose set -e # Exit on any error # Configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" BACKUP_ROOT_DIR="$PROJECT_ROOT/backups" LOG_FILE="$BACKUP_ROOT_DIR/backup.log" # Configuration file path CONFIG_FILE="$SCRIPT_DIR/backup-retention.conf" # Default retention policies (days) - used as fallback if config file is missing or invalid DEFAULT_DAILY_RETENTION=7 DEFAULT_WEEKLY_RETENTION=28 # 4 weeks DEFAULT_MONTHLY_RETENTION=365 # 12 months DEFAULT_PRE_RESTORE_RETENTION=30 DEFAULT_LOG_RETENTION=30 # Default count limits - used as fallback DEFAULT_DAILY_COUNT_LIMIT=7 DEFAULT_WEEKLY_COUNT_LIMIT=4 DEFAULT_MONTHLY_COUNT_LIMIT=12 DEFAULT_PRE_RESTORE_COUNT_LIMIT=5 DEFAULT_SNAPSHOT_COUNT_LIMIT=3 # Default disk usage thresholds DEFAULT_DISK_WARNING_THRESHOLD=85 # Warn at 85% disk usage DEFAULT_DISK_CRITICAL_THRESHOLD=95 # Force cleanup at 95% disk usage # Default settings DEFAULT_EMAIL_NOTIFICATIONS_ENABLED=true DEFAULT_CLEANUP_MODE="both" DEFAULT_CLEANUP_EMPTY_DIRS=true DEFAULT_VERIFY_BEFORE_DELETE=false # Function to load configuration with validation and fallbacks load_config() { # Set defaults first DAILY_RETENTION=$DEFAULT_DAILY_RETENTION WEEKLY_RETENTION=$DEFAULT_WEEKLY_RETENTION MONTHLY_RETENTION=$DEFAULT_MONTHLY_RETENTION PRE_RESTORE_RETENTION=$DEFAULT_PRE_RESTORE_RETENTION LOG_RETENTION=$DEFAULT_LOG_RETENTION DAILY_COUNT_LIMIT=$DEFAULT_DAILY_COUNT_LIMIT WEEKLY_COUNT_LIMIT=$DEFAULT_WEEKLY_COUNT_LIMIT MONTHLY_COUNT_LIMIT=$DEFAULT_MONTHLY_COUNT_LIMIT PRE_RESTORE_COUNT_LIMIT=$DEFAULT_PRE_RESTORE_COUNT_LIMIT SNAPSHOT_COUNT_LIMIT=$DEFAULT_SNAPSHOT_COUNT_LIMIT DISK_WARNING_THRESHOLD=$DEFAULT_DISK_WARNING_THRESHOLD DISK_CRITICAL_THRESHOLD=$DEFAULT_DISK_CRITICAL_THRESHOLD EMAIL_NOTIFICATIONS_ENABLED=$DEFAULT_EMAIL_NOTIFICATIONS_ENABLED CLEANUP_MODE=$DEFAULT_CLEANUP_MODE CLEANUP_EMPTY_DIRS=$DEFAULT_CLEANUP_EMPTY_DIRS VERIFY_BEFORE_DELETE=$DEFAULT_VERIFY_BEFORE_DELETE # Load config file if it exists if [ -f "$CONFIG_FILE" ]; then log "INFO" "Loading retention configuration from: $CONFIG_FILE" # Source the config file with error handling if source "$CONFIG_FILE" 2>/dev/null; then log "INFO" "Configuration loaded successfully" # Validate numeric values and reset to defaults if invalid validate_numeric "DAILY_RETENTION" "$DEFAULT_DAILY_RETENTION" 1 365 validate_numeric "WEEKLY_RETENTION" "$DEFAULT_WEEKLY_RETENTION" 1 730 validate_numeric "MONTHLY_RETENTION" "$DEFAULT_MONTHLY_RETENTION" 1 2555 # ~7 years validate_numeric "PRE_RESTORE_RETENTION" "$DEFAULT_PRE_RESTORE_RETENTION" 1 365 validate_numeric "LOG_RETENTION" "$DEFAULT_LOG_RETENTION" 1 365 validate_numeric "DAILY_COUNT_LIMIT" "$DEFAULT_DAILY_COUNT_LIMIT" 1 100 validate_numeric "WEEKLY_COUNT_LIMIT" "$DEFAULT_WEEKLY_COUNT_LIMIT" 1 100 validate_numeric "MONTHLY_COUNT_LIMIT" "$DEFAULT_MONTHLY_COUNT_LIMIT" 1 100 validate_numeric "PRE_RESTORE_COUNT_LIMIT" "$DEFAULT_PRE_RESTORE_COUNT_LIMIT" 1 50 validate_numeric "SNAPSHOT_COUNT_LIMIT" "$DEFAULT_SNAPSHOT_COUNT_LIMIT" 1 20 validate_numeric "DISK_WARNING_THRESHOLD" "$DEFAULT_DISK_WARNING_THRESHOLD" 50 99 validate_numeric "DISK_CRITICAL_THRESHOLD" "$DEFAULT_DISK_CRITICAL_THRESHOLD" 60 100 # Ensure critical threshold is higher than warning threshold if [ $DISK_CRITICAL_THRESHOLD -le $DISK_WARNING_THRESHOLD ]; then log "WARNING" "Critical threshold ($DISK_CRITICAL_THRESHOLD%) must be higher than warning threshold ($DISK_WARNING_THRESHOLD%). Resetting to defaults." DISK_WARNING_THRESHOLD=$DEFAULT_DISK_WARNING_THRESHOLD DISK_CRITICAL_THRESHOLD=$DEFAULT_DISK_CRITICAL_THRESHOLD fi # Validate boolean values validate_boolean "EMAIL_NOTIFICATIONS_ENABLED" "$DEFAULT_EMAIL_NOTIFICATIONS_ENABLED" validate_boolean "CLEANUP_EMPTY_DIRS" "$DEFAULT_CLEANUP_EMPTY_DIRS" validate_boolean "VERIFY_BEFORE_DELETE" "$DEFAULT_VERIFY_BEFORE_DELETE" # Validate cleanup mode if [[ ! "$CLEANUP_MODE" =~ ^(age_only|count_only|both)$ ]]; then log "WARNING" "Invalid CLEANUP_MODE: $CLEANUP_MODE. Using default: $DEFAULT_CLEANUP_MODE" CLEANUP_MODE=$DEFAULT_CLEANUP_MODE fi else log "WARNING" "Error loading config file. Using default values." fi else log "INFO" "Config file not found: $CONFIG_FILE. Using default retention policies." log "INFO" "Run: cp $CONFIG_FILE.example $CONFIG_FILE to create a customizable config file." fi } # Function to validate numeric configuration values validate_numeric() { local var_name="$1" local default_val="$2" local min_val="$3" local max_val="$4" local current_val eval "current_val=\$$var_name" # Check if it's a valid number if ! [[ "$current_val" =~ ^[0-9]+$ ]]; then log "WARNING" "Invalid $var_name: '$current_val'. Using default: $default_val" eval "$var_name=$default_val" return fi # Check range if [ "$current_val" -lt "$min_val" ] || [ "$current_val" -gt "$max_val" ]; then log "WARNING" "$var_name ($current_val) out of range ($min_val-$max_val). Using default: $default_val" eval "$var_name=$default_val" fi } # Function to validate boolean configuration values validate_boolean() { local var_name="$1" local default_val="$2" local current_val eval "current_val=\$$var_name" # Normalize to lowercase current_val=$(echo "$current_val" | tr '[:upper:]' '[:lower:]') case "$current_val" in true|yes|1|on) eval "$var_name=true" ;; false|no|0|off) eval "$var_name=false" ;; *) log "WARNING" "Invalid $var_name: '$current_val'. Using default: $default_val" eval "$var_name=$default_val" ;; esac } # Create log directory mkdir -p "$BACKUP_ROOT_DIR/logs" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Logging function log() { local level="$1" local message="$2" local timestamp="[$(date '+%Y-%m-%d %H:%M:%S')]" case "$level" in "INFO") echo -e "${timestamp} ${BLUE}[INFO]${NC} $message" | tee -a "$LOG_FILE" ;; "SUCCESS") echo -e "${timestamp} ${GREEN}[SUCCESS]${NC} $message" | tee -a "$LOG_FILE" ;; "WARNING") echo -e "${timestamp} ${YELLOW}[WARNING]${NC} $message" | tee -a "$LOG_FILE" ;; "ERROR") echo -e "${timestamp} ${RED}[ERROR]${NC} $message" | tee -a "$LOG_FILE" ;; *) echo "$timestamp $message" | tee -a "$LOG_FILE" ;; esac } # Function to send email notifications send_notification() { local subject="$1" local message="$2" # Check if email notifications are enabled if [ "$EMAIL_NOTIFICATIONS_ENABLED" != "true" ]; then log "INFO" "Email notifications disabled in config, skipping: $subject" return 0 fi if command -v mail >/dev/null 2>&1; then echo "$message" | mail -s "$subject" "${BACKUP_NOTIFY_EMAIL:-$USER@localhost}" 2>/dev/null || true log "INFO" "Email notification sent: $subject" else log "WARNING" "Mail command not available, notification not sent" fi } # Function to show usage show_usage() { # Load config to show current values load_config echo "Usage: $0 [options]" echo "" echo "Options:" echo " --dry-run - Show what would be deleted without actually deleting" echo " --force - Force cleanup even if disk usage is not critical" echo " --verbose - Show detailed information" echo " --help - Show this help message" echo "" echo "Current Configuration:" echo " Config file: $CONFIG_FILE" if [ -f "$CONFIG_FILE" ]; then echo " Config status: Loaded" else echo " Config status: Using defaults (file not found)" fi echo "" echo "Time-based Retention (days):" echo " Daily backups: $DAILY_RETENTION days" echo " Weekly backups: $WEEKLY_RETENTION days" echo " Monthly backups: $MONTHLY_RETENTION days" echo " Pre-restore: $PRE_RESTORE_RETENTION days" echo " Log files: $LOG_RETENTION days" echo "" echo "Count-based Retention (keep N most recent):" echo " Daily backups: $DAILY_COUNT_LIMIT files" echo " Weekly backups: $WEEKLY_COUNT_LIMIT files" echo " Monthly backups: $MONTHLY_COUNT_LIMIT files" echo " Pre-restore: $PRE_RESTORE_COUNT_LIMIT files" echo " Storage snapshots: $SNAPSHOT_COUNT_LIMIT files" echo "" echo "Disk Space Thresholds:" echo " Warning threshold: $DISK_WARNING_THRESHOLD%" echo " Critical threshold: $DISK_CRITICAL_THRESHOLD%" echo "" echo "Settings:" echo " Email notifications: $EMAIL_NOTIFICATIONS_ENABLED" echo " Cleanup mode: $CLEANUP_MODE" echo " Clean empty dirs: $CLEANUP_EMPTY_DIRS" echo " Verify before delete: $VERIFY_BEFORE_DELETE" echo "" echo "To customize retention policies:" echo " Edit: $CONFIG_FILE" echo "" exit 0 } # Function to check disk usage check_disk_usage() { local backup_dir="$1" local disk_usage=$(df "$backup_dir" | awk 'NR==2 {print substr($5,1,length($5)-1)}') echo "$disk_usage" } # Function to get human readable size get_size() { local path="$1" if [ -f "$path" ]; then du -h "$path" | cut -f1 elif [ -d "$path" ]; then du -sh "$path" | cut -f1 else echo "0B" fi } # Function to cleanup old backups by age cleanup_by_age() { local backup_dir="$1" local retention_days="$2" local backup_type="$3" local pattern="$4" local dry_run="$5" if [ ! -d "$backup_dir" ]; then return 0 fi log "INFO" "Cleaning up $backup_type backups older than $retention_days days" local files_found=0 local files_deleted=0 local total_size_deleted=0 # Find files older than retention period while IFS= read -r -d '' backup_file; do files_found=$((files_found + 1)) local file_size_kb=$(du -k "$backup_file" | cut -f1) local file_size_hr=$(get_size "$backup_file") if [ "$dry_run" = true ]; then if [ "$VERBOSE" = true ]; then log "INFO" "Would delete: $(basename "$backup_file") ($file_size_hr)" fi else if [ "$VERBOSE" = true ]; then log "INFO" "Deleting: $(basename "$backup_file") ($file_size_hr)" fi rm "$backup_file" files_deleted=$((files_deleted + 1)) total_size_deleted=$((total_size_deleted + file_size_kb)) fi done < <(find "$backup_dir" -name "$pattern" -type f -mtime +$retention_days -print0 2>/dev/null) if [ $files_found -gt 0 ]; then local total_size_hr=$(echo "$total_size_deleted" | awk '{printf "%.1fMB", $1/1024}') if [ "$dry_run" = true ]; then log "INFO" "$backup_type: Would delete $files_found files ($total_size_hr)" else log "SUCCESS" "$backup_type: Deleted $files_deleted files ($total_size_hr)" fi else log "INFO" "$backup_type: No old files to clean up" fi return 0 } # Function to cleanup by count (keep only N most recent) cleanup_by_count() { local backup_dir="$1" local keep_count="$2" local backup_type="$3" local pattern="$4" local dry_run="$5" if [ ! -d "$backup_dir" ]; then return 0 fi log "INFO" "Ensuring only $keep_count most recent $backup_type backups are kept" local files_to_delete=($(find "$backup_dir" -name "$pattern" -type f | sort -r | tail -n +$((keep_count + 1)))) if [ ${#files_to_delete[@]} -gt 0 ]; then local total_size_deleted=0 for backup_file in "${files_to_delete[@]}"; do local file_size_kb=$(du -k "$backup_file" | cut -f1) local file_size_hr=$(get_size "$backup_file") if [ "$dry_run" = true ]; then if [ "$VERBOSE" = true ]; then log "INFO" "Would delete (count): $(basename "$backup_file") ($file_size_hr)" fi else if [ "$VERBOSE" = true ]; then log "INFO" "Deleting (count): $(basename "$backup_file") ($file_size_hr)" fi rm "$backup_file" total_size_deleted=$((total_size_deleted + file_size_kb)) fi done local total_size_hr=$(echo "$total_size_deleted" | awk '{printf "%.1fMB", $1/1024}') if [ "$dry_run" = true ]; then log "INFO" "$backup_type: Would delete ${#files_to_delete[@]} excess files ($total_size_hr)" else log "SUCCESS" "$backup_type: Deleted ${#files_to_delete[@]} excess files ($total_size_hr)" fi else log "INFO" "$backup_type: No excess files to clean up" fi return 0 } # Function to clean up empty directories cleanup_empty_dirs() { local backup_root="$1" local dry_run="$2" log "INFO" "Cleaning up empty directories" local empty_dirs=($(find "$backup_root" -type d -empty 2>/dev/null)) if [ ${#empty_dirs[@]} -gt 0 ]; then for empty_dir in "${empty_dirs[@]}"; do # Skip the root backup directories if [[ "$empty_dir" =~ (database|storage|logs)$ ]]; then continue fi if [ "$dry_run" = true ]; then if [ "$VERBOSE" = true ]; then log "INFO" "Would remove empty directory: $empty_dir" fi else if [ "$VERBOSE" = true ]; then log "INFO" "Removing empty directory: $empty_dir" fi rmdir "$empty_dir" 2>/dev/null || true fi done if [ "$dry_run" = true ]; then log "INFO" "Would remove ${#empty_dirs[@]} empty directories" else log "SUCCESS" "Removed empty directories" fi else log "INFO" "No empty directories to clean up" fi } # Function to generate cleanup report generate_report() { local backup_root="$1" log "INFO" "Backup storage report:" if [ -d "$backup_root" ]; then # Database backups if [ -d "$backup_root/database" ]; then local db_count=$(find "$backup_root/database" -name "*.sql.gz" | wc -l) local db_size=$(get_size "$backup_root/database") log "INFO" " Database backups: $db_count files ($db_size)" fi # Storage backups if [ -d "$backup_root/storage" ]; then local storage_count=$(find "$backup_root/storage" -name "*.tar.gz" | wc -l) local storage_size=$(get_size "$backup_root/storage") log "INFO" " Storage backups: $storage_count files ($storage_size)" fi # Total size local total_size=$(get_size "$backup_root") log "INFO" " Total backup size: $total_size" # Disk usage local disk_usage=$(check_disk_usage "$backup_root") local available_space=$(df -h "$backup_root" | awk 'NR==2 {print $4}') log "INFO" " Disk usage: ${disk_usage}% (${available_space} available)" # Oldest and newest backups local oldest_backup=$(find "$backup_root" -name "*.gz" -type f -printf '%T@ %p\n' 2>/dev/null | sort -n | head -n 1 | cut -d' ' -f2-) local newest_backup=$(find "$backup_root" -name "*.gz" -type f -printf '%T@ %p\n' 2>/dev/null | sort -n | tail -n 1 | cut -d' ' -f2-) if [ -n "$oldest_backup" ]; then local oldest_date=$(date -r "$oldest_backup" '+%Y-%m-%d %H:%M:%S') log "INFO" " Oldest backup: $(basename "$oldest_backup") ($oldest_date)" fi if [ -n "$newest_backup" ]; then local newest_date=$(date -r "$newest_backup" '+%Y-%m-%d %H:%M:%S') log "INFO" " Newest backup: $(basename "$newest_backup") ($newest_date)" fi else log "WARNING" "No backup directory found" fi } # Parse command line arguments DRY_RUN=false FORCE=false VERBOSE=false while [[ $# -gt 0 ]]; do case $1 in --dry-run) DRY_RUN=true shift ;; --force) FORCE=true shift ;; --verbose) VERBOSE=true shift ;; --help) show_usage ;; *) log "ERROR" "Unknown option: $1" show_usage ;; esac done # Main execution main() { # Load configuration before starting load_config log "INFO" "============================================" if [ "$DRY_RUN" = true ]; then log "INFO" "Starting backup cleanup (DRY RUN)" else log "INFO" "Starting backup cleanup" fi log "INFO" "Time: $(date)" log "INFO" "============================================" local start_time=$(date +%s) # Check if backup directory exists if [ ! -d "$BACKUP_ROOT_DIR" ]; then log "WARNING" "Backup directory not found: $BACKUP_ROOT_DIR" exit 0 fi # Check disk usage local disk_usage=$(check_disk_usage "$BACKUP_ROOT_DIR") log "INFO" "Current disk usage: ${disk_usage}%" local should_cleanup=false if [ $disk_usage -ge $DISK_CRITICAL_THRESHOLD ]; then log "WARNING" "Disk usage critical (${disk_usage}% >= ${DISK_CRITICAL_THRESHOLD}%), forcing cleanup" send_notification "Timebank Cleanup Critical - Aggressive Cleanup Started" \ "CRITICAL: Disk usage has reached ${disk_usage}% (>= ${DISK_CRITICAL_THRESHOLD}%) Aggressive backup cleanup has been automatically triggered to free disk space. Location: $BACKUP_ROOT_DIR Current usage: ${disk_usage}% Critical threshold: ${DISK_CRITICAL_THRESHOLD}% Time: $(date) This is an automated cleanup to prevent disk space issues. Please check the system if this occurs frequently." should_cleanup=true elif [ $disk_usage -ge $DISK_WARNING_THRESHOLD ]; then log "WARNING" "Disk usage high (${disk_usage}% >= ${DISK_WARNING_THRESHOLD}%)" send_notification "Timebank Cleanup Warning - Low Disk Space" \ "WARNING: Disk usage is getting high at ${disk_usage}% (>= ${DISK_WARNING_THRESHOLD}%) Backup cleanup will be performed to prevent potential disk space issues. Location: $BACKUP_ROOT_DIR Current usage: ${disk_usage}% Warning threshold: ${DISK_WARNING_THRESHOLD}% Critical threshold: ${DISK_CRITICAL_THRESHOLD}% Time: $(date) Please monitor disk usage and consider expanding storage if warnings occur frequently." should_cleanup=true elif [ "$FORCE" = true ]; then log "INFO" "Force cleanup requested" should_cleanup=true else log "INFO" "Disk usage normal, running standard cleanup" should_cleanup=true fi if [ "$should_cleanup" = true ]; then # Cleanup database backups if [ -d "$BACKUP_ROOT_DIR/database" ]; then # Load environment for database name if [ -f "$PROJECT_ROOT/.env" ]; then source "$SCRIPT_DIR/load-env.sh" load_env "$PROJECT_ROOT/.env" fi local db_pattern="*.sql.gz" if [ -n "$DB_DATABASE" ]; then db_pattern="${DB_DATABASE}_*.sql.gz" fi cleanup_by_age "$BACKUP_ROOT_DIR/database/daily" $DAILY_RETENTION "daily database" "$db_pattern" "$DRY_RUN" cleanup_by_age "$BACKUP_ROOT_DIR/database/weekly" $WEEKLY_RETENTION "weekly database" "$db_pattern" "$DRY_RUN" cleanup_by_age "$BACKUP_ROOT_DIR/database/monthly" $MONTHLY_RETENTION "monthly database" "$db_pattern" "$DRY_RUN" cleanup_by_age "$BACKUP_ROOT_DIR/database/pre-restore" $PRE_RESTORE_RETENTION "pre-restore database" "*.sql.gz" "$DRY_RUN" # Also enforce count limits cleanup_by_count "$BACKUP_ROOT_DIR/database/daily" $DAILY_COUNT_LIMIT "daily database" "$db_pattern" "$DRY_RUN" cleanup_by_count "$BACKUP_ROOT_DIR/database/weekly" $WEEKLY_COUNT_LIMIT "weekly database" "$db_pattern" "$DRY_RUN" cleanup_by_count "$BACKUP_ROOT_DIR/database/monthly" $MONTHLY_COUNT_LIMIT "monthly database" "$db_pattern" "$DRY_RUN" fi # Cleanup storage backups if [ -d "$BACKUP_ROOT_DIR/storage" ]; then cleanup_by_age "$BACKUP_ROOT_DIR/storage/daily" $DAILY_RETENTION "daily storage" "*.tar.gz" "$DRY_RUN" cleanup_by_age "$BACKUP_ROOT_DIR/storage/weekly" $WEEKLY_RETENTION "weekly storage" "*.tar.gz" "$DRY_RUN" cleanup_by_age "$BACKUP_ROOT_DIR/storage/monthly" $MONTHLY_RETENTION "monthly storage" "*.tar.gz" "$DRY_RUN" cleanup_by_age "$BACKUP_ROOT_DIR/storage/pre-restore" $PRE_RESTORE_RETENTION "pre-restore storage" "*.tar.gz" "$DRY_RUN" # Count limits for storage cleanup_by_count "$BACKUP_ROOT_DIR/storage/daily" $DAILY_COUNT_LIMIT "daily storage" "*.tar.gz" "$DRY_RUN" cleanup_by_count "$BACKUP_ROOT_DIR/storage/weekly" $WEEKLY_COUNT_LIMIT "weekly storage" "*.tar.gz" "$DRY_RUN" cleanup_by_count "$BACKUP_ROOT_DIR/storage/monthly" $MONTHLY_COUNT_LIMIT "monthly storage" "*.tar.gz" "$DRY_RUN" # Cleanup old snapshots cleanup_by_count "$BACKUP_ROOT_DIR/storage/snapshots" $SNAPSHOT_COUNT_LIMIT "storage snapshots" "*" "$DRY_RUN" fi # Cleanup empty directories cleanup_empty_dirs "$BACKUP_ROOT_DIR" "$DRY_RUN" # Cleanup old log files if [ -d "$BACKUP_ROOT_DIR/logs" ]; then cleanup_by_age "$BACKUP_ROOT_DIR/logs" $LOG_RETENTION "log files" "*.log" "$DRY_RUN" fi fi # Generate final report generate_report "$BACKUP_ROOT_DIR" # Calculate execution time local end_time=$(date +%s) local execution_time=$((end_time - start_time)) local execution_time_formatted=$(date -d@$execution_time -u +%H:%M:%S) # Send completion summary email if cleanup was triggered by disk space conditions local final_disk_usage=$(check_disk_usage "$BACKUP_ROOT_DIR") if [ "$should_cleanup" = true ] && ([ $disk_usage -ge $DISK_WARNING_THRESHOLD ] || [ "$FORCE" = true ]); then local cleanup_type="Standard" if [ $disk_usage -ge $DISK_CRITICAL_THRESHOLD ]; then cleanup_type="Aggressive (Critical)" elif [ $disk_usage -ge $DISK_WARNING_THRESHOLD ]; then cleanup_type="Warning Level" elif [ "$FORCE" = true ]; then cleanup_type="Manual Force" fi # Get backup statistics local db_count=0 local db_size="0B" local storage_count=0 local storage_size="0B" local total_size="Unknown" if [ -d "$BACKUP_ROOT_DIR/database" ]; then db_count=$(find "$BACKUP_ROOT_DIR/database" -name "*.sql.gz" 2>/dev/null | wc -l) db_size=$(get_size "$BACKUP_ROOT_DIR/database") fi if [ -d "$BACKUP_ROOT_DIR/storage" ]; then storage_count=$(find "$BACKUP_ROOT_DIR/storage" -name "*.tar.gz" 2>/dev/null | wc -l) storage_size=$(get_size "$BACKUP_ROOT_DIR/storage") fi if [ -d "$BACKUP_ROOT_DIR" ]; then total_size=$(get_size "$BACKUP_ROOT_DIR") fi local mode_text="cleanup completed" if [ "$DRY_RUN" = true ]; then mode_text="cleanup analysis completed (DRY RUN)" fi send_notification "Timebank Backup Cleanup Completed" \ "Backup $mode_text successfully. Cleanup Details: - Type: $cleanup_type cleanup - Duration: $execution_time_formatted - Initial disk usage: ${disk_usage}% - Final disk usage: ${final_disk_usage}% Current Backup Status: - Database backups: $db_count files ($db_size) - Storage backups: $storage_count files ($storage_size) - Total backup size: $total_size Location: $BACKUP_ROOT_DIR Completed: $(date) For detailed logs, check: $LOG_FILE" fi if [ "$DRY_RUN" = true ]; then log "SUCCESS" "Backup cleanup analysis completed in $execution_time_formatted" else log "SUCCESS" "Backup cleanup completed in $execution_time_formatted" fi log "INFO" "============================================" log "INFO" "Cleanup process finished" log "INFO" "============================================" } # Run main function main