#!/bin/bash # Laravel Timebank Database Backup Script # Backs up MySQL database with compression and rotation # Usage: ./backup-database.sh [backup_type] # backup_type: daily (default), weekly, monthly 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" # Create backup directories mkdir -p "$BACKUP_ROOT_DIR"/{database/{daily,weekly,monthly},logs} # Logging function log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } # Load environment loader source "$SCRIPT_DIR/load-env.sh" # Load environment variables if ! load_env "$PROJECT_ROOT/.env"; then log "ERROR: .env file not found in $PROJECT_ROOT" exit 1 fi # Validate required environment variables if [ -z "$DB_DATABASE" ] || [ -z "$DB_USERNAME" ] || [ -z "$DB_HOST" ]; then log "ERROR: Required database environment variables not found (DB_DATABASE, DB_USERNAME, DB_HOST)" exit 1 fi # Set backup type (daily, weekly, monthly) BACKUP_TYPE="${1:-daily}" TIMESTAMP=$(date '+%Y%m%d_%H%M%S') BACKUP_DIR="$BACKUP_ROOT_DIR/database/$BACKUP_TYPE" BACKUP_FILE="$BACKUP_DIR/${DB_DATABASE}_${BACKUP_TYPE}_${TIMESTAMP}.sql" COMPRESSED_FILE="${BACKUP_FILE}.gz" # Create backup type directory mkdir -p "$BACKUP_DIR" log "Starting $BACKUP_TYPE database backup for $DB_DATABASE" # Perform database backup log "Creating database dump..." # Create MySQL configuration file for secure password handling MYSQL_CNF_FILE="/tmp/mysql_backup_$$.cnf" cat > "$MYSQL_CNF_FILE" < "$BACKUP_FILE" # Clean up the temporary config file rm -f "$MYSQL_CNF_FILE" # Compress the backup log "Compressing backup..." gzip "$BACKUP_FILE" # Verify the compressed backup exists and has content if [ -f "$COMPRESSED_FILE" ] && [ -s "$COMPRESSED_FILE" ]; then BACKUP_SIZE=$(du -h "$COMPRESSED_FILE" | cut -f1) log "SUCCESS: Database backup completed - $COMPRESSED_FILE ($BACKUP_SIZE)" else log "ERROR: Backup verification failed - $COMPRESSED_FILE" exit 1 fi # Retention cleanup based on backup type cleanup_old_backups() { local backup_dir="$1" local keep_count="$2" local pattern="$3" log "Cleaning up old $BACKUP_TYPE backups (keeping $keep_count most recent)" # Remove old backups, keeping only the specified number ls -t "$backup_dir"/$pattern 2>/dev/null | tail -n +$((keep_count + 1)) | while read -r old_backup; do if [ -f "$backup_dir/$old_backup" ]; then rm "$backup_dir/$old_backup" log "Removed old backup: $old_backup" fi done } # Apply retention policy case "$BACKUP_TYPE" in daily) cleanup_old_backups "$BACKUP_DIR" 7 "${DB_DATABASE}_daily_*.sql.gz" ;; weekly) cleanup_old_backups "$BACKUP_DIR" 4 "${DB_DATABASE}_weekly_*.sql.gz" ;; monthly) cleanup_old_backups "$BACKUP_DIR" 12 "${DB_DATABASE}_monthly_*.sql.gz" ;; esac # Generate backup report TOTAL_BACKUPS=$(find "$BACKUP_ROOT_DIR/database" -name "*.sql.gz" | wc -l) TOTAL_SIZE=$(du -sh "$BACKUP_ROOT_DIR/database" | cut -f1) log "Backup summary: $TOTAL_BACKUPS total backups, $TOTAL_SIZE total size" log "$BACKUP_TYPE database backup completed successfully" # Send notification if command -v mail >/dev/null 2>&1; then echo "Database backup completed successfully at $(date)" | mail -s "Timebank DB Backup Success" "${BACKUP_NOTIFY_EMAIL:-$USER@localhost}" 2>/dev/null || true fi