Snapshot of main at 9f39a6e1e (2026-11-02)
This commit is contained in:
commit
6ccf1792d7
2580 files changed
+1040202
No files matched your search
Executable
+235
@@ -0,0 +1,235 @@
|
||||
#!/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
|
||||
|
||||
# This installation's platform folder (config, theme, overrides, public files; outside git, see
|
||||
# App\Providers\PlatformServiceProvider). Small, so a full archive each run, with the same retention as above.
|
||||
PLATFORM_DIR=$(grep -E '^PLATFORM_PATH=' "$PROJECT_ROOT/.env" 2>/dev/null | tail -1 | cut -d= -f2- | tr -d "\"'")
|
||||
PLATFORM_DIR="${PLATFORM_DIR:-platform}"
|
||||
[[ "$PLATFORM_DIR" = /* ]] || PLATFORM_DIR="$PROJECT_ROOT/$PLATFORM_DIR"
|
||||
if [ -d "$PLATFORM_DIR" ]; then
|
||||
platform_backup_dir="$BACKUP_ROOT_DIR/platform/$BACKUP_TYPE"
|
||||
platform_archive="$platform_backup_dir/platform_${BACKUP_TYPE}_${TIMESTAMP}.tar.gz"
|
||||
mkdir -p "$platform_backup_dir"
|
||||
if tar -czf "$platform_archive" -C "$(dirname "$PLATFORM_DIR")" "$(basename "$PLATFORM_DIR")" 2>>"$LOG_FILE"; then
|
||||
log "Platform folder backed up: $platform_archive ($(du -h "$platform_archive" | cut -f1))"
|
||||
case "$BACKUP_TYPE" in
|
||||
daily) platform_keep=7 ;; weekly) platform_keep=4 ;; monthly) platform_keep=12 ;; *) platform_keep=2 ;;
|
||||
esac
|
||||
find "$platform_backup_dir" -name "platform_${BACKUP_TYPE}_*.tar.gz" -type f | sort -r | tail -n +$((platform_keep + 1)) | xargs -r rm -f
|
||||
else
|
||||
rm -f "$platform_archive"
|
||||
log "WARNING: backing up the platform folder $PLATFORM_DIR failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 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
|
||||
Reference in new issue
Block a user