214 lines
6.4 KiB
Bash
Executable File
214 lines
6.4 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Laravel Timebank Storage Backup Script
|
|
# Backs up storage directory with incremental rsync and compression
|
|
# Usage: ./backup-storage.sh [backup_type]
|
|
# backup_type: daily (default), weekly, monthly, full
|
|
|
|
set -e # Exit on any error
|
|
|
|
# Configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
STORAGE_DIR="$PROJECT_ROOT/storage"
|
|
BACKUP_ROOT_DIR="$PROJECT_ROOT/backups"
|
|
LOG_FILE="$BACKUP_ROOT_DIR/backup.log"
|
|
|
|
# Create backup directories
|
|
mkdir -p "$BACKUP_ROOT_DIR"/{storage/{daily,weekly,monthly,snapshots},logs}
|
|
|
|
# Logging function
|
|
log() {
|
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Check if storage directory exists
|
|
if [ ! -d "$STORAGE_DIR" ]; then
|
|
log "ERROR: Storage directory not found: $STORAGE_DIR"
|
|
exit 1
|
|
fi
|
|
|
|
# Set backup type
|
|
BACKUP_TYPE="${1:-daily}"
|
|
TIMESTAMP=$(date '+%Y%m%d_%H%M%S')
|
|
BACKUP_DIR="$BACKUP_ROOT_DIR/storage/$BACKUP_TYPE"
|
|
SNAPSHOT_DIR="$BACKUP_ROOT_DIR/storage/snapshots"
|
|
|
|
# Create directories
|
|
mkdir -p "$BACKUP_DIR" "$SNAPSHOT_DIR"
|
|
|
|
log "Starting $BACKUP_TYPE storage backup"
|
|
|
|
# Rsync exclude patterns for Laravel storage
|
|
RSYNC_EXCLUDES="
|
|
--exclude=framework/cache/*
|
|
--exclude=framework/sessions/*
|
|
--exclude=framework/testing/*
|
|
--exclude=framework/views/*
|
|
--exclude=logs/*.log
|
|
--exclude=debugbar/*
|
|
--exclude=app/backup/*
|
|
--exclude=*/livewire-tmp/*
|
|
--exclude=*.tmp
|
|
--exclude=.DS_Store
|
|
--exclude=Thumbs.db
|
|
"
|
|
|
|
# Function for incremental backup using rsync
|
|
incremental_backup() {
|
|
local target_dir="$1"
|
|
local snapshot_name="$2"
|
|
|
|
log "Performing incremental backup to $target_dir"
|
|
|
|
# Create current snapshot directory
|
|
local current_snapshot="$SNAPSHOT_DIR/$snapshot_name"
|
|
mkdir -p "$current_snapshot"
|
|
|
|
# Rsync with hard links to previous snapshot for space efficiency
|
|
local link_dest_option=""
|
|
local latest_snapshot=$(find "$SNAPSHOT_DIR" -maxdepth 1 -type d -name "${BACKUP_TYPE}_*" | sort -r | head -n 1)
|
|
|
|
if [ -n "$latest_snapshot" ] && [ "$latest_snapshot" != "$current_snapshot" ]; then
|
|
link_dest_option="--link-dest=$latest_snapshot"
|
|
fi
|
|
|
|
rsync -av \
|
|
--delete \
|
|
$RSYNC_EXCLUDES \
|
|
$link_dest_option \
|
|
"$STORAGE_DIR/" \
|
|
"$current_snapshot/" \
|
|
2>&1 | tee -a "$LOG_FILE"
|
|
|
|
# Create compressed archive for long-term storage
|
|
local archive_name="${snapshot_name}.tar.gz"
|
|
local archive_path="$target_dir/$archive_name"
|
|
|
|
log "Creating compressed archive: $archive_name"
|
|
tar -czf "$archive_path" -C "$SNAPSHOT_DIR" "$snapshot_name" 2>&1 | tee -a "$LOG_FILE"
|
|
|
|
# Verify archive
|
|
if [ -f "$archive_path" ] && [ -s "$archive_path" ]; then
|
|
local archive_size=$(du -h "$archive_path" | cut -f1)
|
|
log "Archive created successfully: $archive_path ($archive_size)"
|
|
|
|
# Keep snapshots for recent backups only (last 3)
|
|
if [ "$BACKUP_TYPE" = "daily" ]; then
|
|
# Clean up old daily snapshots (keep last 3)
|
|
find "$SNAPSHOT_DIR" -maxdepth 1 -type d -name "daily_*" | sort -r | tail -n +4 | xargs -r rm -rf
|
|
fi
|
|
|
|
return 0
|
|
else
|
|
log "ERROR: Archive creation failed or archive is empty"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Function for full backup (copy entire directory)
|
|
full_backup() {
|
|
local target_dir="$1"
|
|
local backup_name="storage_full_${TIMESTAMP}"
|
|
local backup_path="$target_dir/${backup_name}.tar.gz"
|
|
|
|
log "Performing full storage backup"
|
|
|
|
# Create temporary directory for clean backup
|
|
local temp_dir="/tmp/timebank_storage_backup_$$"
|
|
mkdir -p "$temp_dir"
|
|
|
|
# Copy storage with rsync (excluding unwanted files)
|
|
rsync -av $RSYNC_EXCLUDES "$STORAGE_DIR/" "$temp_dir/" 2>&1 | tee -a "$LOG_FILE"
|
|
|
|
# Create compressed archive
|
|
log "Creating compressed archive: $backup_name.tar.gz"
|
|
tar -czf "$backup_path" -C "/tmp" "timebank_storage_backup_$$" 2>&1 | tee -a "$LOG_FILE"
|
|
|
|
# Cleanup temporary directory
|
|
rm -rf "$temp_dir"
|
|
|
|
# Verify archive
|
|
if [ -f "$backup_path" ] && [ -s "$backup_path" ]; then
|
|
local archive_size=$(du -h "$backup_path" | cut -f1)
|
|
log "Full backup completed: $backup_path ($archive_size)"
|
|
return 0
|
|
else
|
|
log "ERROR: Full backup failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Perform backup based on type
|
|
case "$BACKUP_TYPE" in
|
|
daily)
|
|
incremental_backup "$BACKUP_DIR" "daily_${TIMESTAMP}"
|
|
;;
|
|
weekly)
|
|
incremental_backup "$BACKUP_DIR" "weekly_${TIMESTAMP}"
|
|
;;
|
|
monthly)
|
|
incremental_backup "$BACKUP_DIR" "monthly_${TIMESTAMP}"
|
|
;;
|
|
full)
|
|
full_backup "$BACKUP_DIR"
|
|
;;
|
|
*)
|
|
log "ERROR: Invalid backup type: $BACKUP_TYPE (valid: daily, weekly, monthly, full)"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Cleanup function
|
|
cleanup_old_backups() {
|
|
local backup_dir="$1"
|
|
local keep_count="$2"
|
|
local pattern="$3"
|
|
|
|
log "Cleaning up old $BACKUP_TYPE storage backups (keeping $keep_count most recent)"
|
|
|
|
find "$backup_dir" -name "$pattern" -type f | sort -r | tail -n +$((keep_count + 1)) | while read -r old_backup; do
|
|
if [ -f "$old_backup" ]; then
|
|
rm "$old_backup"
|
|
log "Removed old backup: $(basename "$old_backup")"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Apply retention policy
|
|
case "$BACKUP_TYPE" in
|
|
daily)
|
|
cleanup_old_backups "$BACKUP_DIR" 7 "daily_*.tar.gz"
|
|
;;
|
|
weekly)
|
|
cleanup_old_backups "$BACKUP_DIR" 4 "weekly_*.tar.gz"
|
|
;;
|
|
monthly)
|
|
cleanup_old_backups "$BACKUP_DIR" 12 "monthly_*.tar.gz"
|
|
;;
|
|
full)
|
|
cleanup_old_backups "$BACKUP_DIR" 2 "storage_full_*.tar.gz"
|
|
;;
|
|
esac
|
|
|
|
# Generate storage usage report
|
|
log "Storage backup summary:"
|
|
if [ -d "$BACKUP_ROOT_DIR/storage" ]; then
|
|
find "$BACKUP_ROOT_DIR/storage" -name "*.tar.gz" -exec basename {} \; | sort | while read -r backup; do
|
|
backup_path="$BACKUP_ROOT_DIR/storage"/*/"$backup"
|
|
if [ -f $backup_path ]; then
|
|
size=$(du -h $backup_path | cut -f1)
|
|
log " $backup ($size)"
|
|
fi
|
|
done
|
|
|
|
total_size=$(du -sh "$BACKUP_ROOT_DIR/storage" | cut -f1)
|
|
log "Total storage backups size: $total_size"
|
|
fi
|
|
|
|
log "$BACKUP_TYPE storage backup completed successfully"
|
|
|
|
# Send notification
|
|
if command -v mail >/dev/null 2>&1; then
|
|
echo "Storage backup completed successfully at $(date)" | mail -s "Timebank Storage Backup Success" "${BACKUP_NOTIFY_EMAIL:-$USER@localhost}" 2>/dev/null || true
|
|
fi |