fix: backup monitoring and PATH environment issues
- Fix backup_monitor.sh find command to sort by modification time - Fix grep -oP syntax error (change to grep -oE) - Adjust tier rotation threshold from -mtime +7 to +6 - Add backup_all.sh script with PATH fixes for crontab - Add mysql-client/bin to PATH for mysqldump command - Fix backup status check for v2 naming patterns
This commit is contained in:
@@ -92,7 +92,7 @@ check_backup_status() {
|
||||
if [ -d "$service_backup_dir" ]; then
|
||||
file_count=$(find "$service_backup_dir" -type f 2>/dev/null | wc -l)
|
||||
size=$(du -sb "$service_backup_dir" 2>/dev/null | cut -f1)
|
||||
latest_file=$(find "$service_backup_dir" -type f \( -name "*.tar.gz" -o -name "*.sql.gz" -o -name "*.rdb" \) 2>/dev/null | head -1)
|
||||
latest_file=$(find "$service_backup_dir" -type f \( -name "*.tar.gz" -o -name "*.sql.gz" -o -name "*.rdb" \) -printf "%T@ %p\n" 2>/dev/null | sort -rn | head -1 | cut -d' ' -f2-)
|
||||
|
||||
# 處理 size 為空或 0 的情況
|
||||
if [ -z "$size" ] || [ "$size" = "0" ]; then
|
||||
@@ -271,12 +271,12 @@ tier_backups() {
|
||||
|
||||
# 7天前: daily -> weekly
|
||||
# 命名格式: {service}_{type}_{YYYYMMDD}_{HHMMSS}.{ext}
|
||||
find "$BACKUP_BASE/daily" -type f -mtime +7 | while read -r file; do
|
||||
find "$BACKUP_BASE/daily" -type f -mtime +6 | while read -r file; do
|
||||
service=$(basename "$(dirname "$file")")
|
||||
|
||||
# 解析時間戳
|
||||
filename=$(basename "$file")
|
||||
timestamp=$(echo "$filename" | grep -oP '\d{8}_\d{6}' || echo "")
|
||||
timestamp=$(echo "$filename" | grep -oE '[0-9]{8}_[0-9]{6}' || echo "")
|
||||
|
||||
if [ -n "$timestamp" ]; then
|
||||
year=${timestamp:0:4}
|
||||
|
||||
821
scripts/backup_all.sh
Executable file
821
scripts/backup_all.sh
Executable file
@@ -0,0 +1,821 @@
|
||||
#!/bin/bash
|
||||
export PATH="/usr/local/bin:/opt/homebrew/bin:/opt/homebrew/opt/postgresql@18/bin:/usr/bin:/bin:/sbin:/opt/homebrew/opt/mysql-client/bin:$PATH"
|
||||
|
||||
#===============================================================================
|
||||
# Momentry 統一備份腳本
|
||||
# 路徑: /Users/accusys/momentry/scripts/backup_all.sh
|
||||
#
|
||||
# 命名規範 (v2):
|
||||
# {service}_{type}_v2_{YYYYMMDD}_{HHMMSS}.{ext}
|
||||
#
|
||||
# 版本說明:
|
||||
# v1: 初始備份架構(不包含新架構組件)
|
||||
# v2: 新架構備份(包含 monitor_jobs, processor_results, Output 目錄)
|
||||
#
|
||||
# 使用方式:
|
||||
# ./backup_all.sh [service|all] [type] [timestamp]
|
||||
#
|
||||
# 參數:
|
||||
# service - 特定服務 (postgresql, redis, mariadb, wordpress, n8n, qdrant, gitea, ollama, caddy, sftpgo, mongodb, php, momentry_output)
|
||||
# all - 備份所有服務 (默認)
|
||||
# type - 備份類型 (full, db, cfg, data)
|
||||
# timestamp - 指定時間戳 (格式: YYYYMMDD_HHMMSS)
|
||||
#
|
||||
# 示例:
|
||||
# ./backup_all.sh # 備份所有服務 (v2)
|
||||
# ./backup_all.sh postgresql # 只備份 PostgreSQL
|
||||
# ./backup_all.sh all full # 完整備份所有服務 (v2)
|
||||
# ./backup_all.sh mariadb db # 只備份 MariaDB 數據庫
|
||||
# ./backup_all.sh restore 20260316_101215 # 恢復到指定斷點
|
||||
#
|
||||
# ⚠️ v2 版本差異:
|
||||
# - 新增 monitor_jobs, processor_results 表
|
||||
# - 新增 Output 目錄備份
|
||||
# - MongoDB 路徑修正
|
||||
#
|
||||
# 排程範例 (crontab):
|
||||
# # 每天凌晨 3 點執行所有備份
|
||||
# 0 3 * * * /Users/accusys/momentry/scripts/backup_all.sh >> /Users/accusys/momentry/log/backup.log 2>&1
|
||||
#
|
||||
# # 每週日凌晨 3 點執行完整備份
|
||||
# 0 3 * * 0 /Users/accusys/momentry/scripts/backup_all.sh all full >> /Users/accusys/momentry/log/backup.log 2>&1
|
||||
#===============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# 載入密碼配置
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if [ -f "$SCRIPT_DIR/load_credentials.sh" ]; then
|
||||
source "$SCRIPT_DIR/load_credentials.sh"
|
||||
fi
|
||||
|
||||
# 確保路徑正確(Crontab 環境可能缺少 PATH)
|
||||
export PATH="/usr/local/bin:/opt/homebrew/bin:/opt/homebrew/opt/postgresql@18/bin:/sbin:/usr/sbin:/usr/bin:/bin:/opt/homebrew/opt/mysql-client/bin"
|
||||
|
||||
# 顏色定義
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 路徑配置
|
||||
BACKUP_ROOT="/Users/accusys/momentry/backup/daily"
|
||||
LOG_DIR="/Users/accusys/momentry/log"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# 備份版本 (v2 = 新架構)
|
||||
BACKUP_VERSION="v2"
|
||||
|
||||
# 時間戳 (v2 格式: v2_YYYYMMDD_HHMMSS)
|
||||
if [ -n "$3" ]; then
|
||||
TIMESTAMP="$3"
|
||||
else
|
||||
TIMESTAMP="${BACKUP_VERSION}_$(date +%Y%m%d_%H%M%S)"
|
||||
fi
|
||||
|
||||
# 服務列表 (v2 新增 momentry_output)
|
||||
SERVICES=("postgresql" "redis" "mariadb" "wordpress" "n8n" "qdrant" "gitea" "ollama" "caddy" "sftpgo" "mongodb" "php" "momentry_output")
|
||||
|
||||
#===============================================================================
|
||||
# 日誌函數
|
||||
#===============================================================================
|
||||
log() {
|
||||
echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_DIR/backup.log"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] ✅ $1${NC}" | tee -a "$LOG_DIR/backup.log"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] ❌ $1${NC}" | tee -a "$LOG_DIR/backup.log"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] ⚠️ $1${NC}" | tee -a "$LOG_DIR/backup.log"
|
||||
}
|
||||
|
||||
#===============================================================================
|
||||
# 通用函數
|
||||
#===============================================================================
|
||||
ensure_backup_dir() {
|
||||
local service=$1
|
||||
mkdir -p "$BACKUP_ROOT/$service"
|
||||
}
|
||||
|
||||
backup_file() {
|
||||
local service=$1
|
||||
local type=$2
|
||||
local file=$3
|
||||
|
||||
ensure_backup_dir "$service"
|
||||
|
||||
if [ -f "$file" ]; then
|
||||
local filename=$(basename "$file")
|
||||
local dest="$BACKUP_ROOT/$service/${service}_${type}_${TIMESTAMP}_${filename}"
|
||||
cp "$file" "$dest"
|
||||
|
||||
# 壓縮
|
||||
if [[ "$filename" == *.sql ]]; then
|
||||
gzip "$dest"
|
||||
dest="${dest}.gz"
|
||||
fi
|
||||
|
||||
# SHA256
|
||||
sha256sum "$dest" >"${dest}.sha256"
|
||||
|
||||
log_success "$service $type: $(basename "$dest")"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
backup_directory() {
|
||||
local service=$1
|
||||
local type=$2
|
||||
local dir=$3
|
||||
|
||||
ensure_backup_dir "$service"
|
||||
|
||||
if [ -d "$dir" ]; then
|
||||
local dest="$BACKUP_ROOT/$service/${service}_${type}_${TIMESTAMP}.tar.gz"
|
||||
tar -czf "$dest" -C "$(dirname "$dir")" "$(basename "$dir")" 2>/dev/null || true
|
||||
|
||||
# SHA256
|
||||
sha256sum "$dest" >"${dest}.sha256"
|
||||
|
||||
log_success "$service $type: $(basename "$dest")"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
#===============================================================================
|
||||
# 服務備份函數
|
||||
#===============================================================================
|
||||
|
||||
# PostgreSQL
|
||||
backup_postgresql() {
|
||||
local type=${1:-db}
|
||||
log "開始 PostgreSQL 備份..."
|
||||
|
||||
# momentry 數據庫
|
||||
PGPASSWORD="$PG_PASSWORD" pg_dump -U "$PG_USER" -d momentry | gzip >"$BACKUP_ROOT/postgresql/postgresql_db_momentry_${TIMESTAMP}.sql.gz"
|
||||
sha256sum "$BACKUP_ROOT/postgresql/postgresql_db_momentry_${TIMESTAMP}.sql.gz" >"$BACKUP_ROOT/postgresql/postgresql_db_${TIMESTAMP}.sha256"
|
||||
|
||||
# video_register 數據庫
|
||||
PGPASSWORD="$PG_PASSWORD" pg_dump -U "$PG_USER" -d video_register | gzip >"$BACKUP_ROOT/postgresql/postgresql_db_video_register_${TIMESTAMP}.sql.gz"
|
||||
sha256sum "$BACKUP_ROOT/postgresql/postgresql_db_video_register_${TIMESTAMP}.sql.gz" >>"$BACKUP_ROOT/postgresql/postgresql_db_${TIMESTAMP}.sha256"
|
||||
|
||||
log_success "PostgreSQL: 數據庫備份完成"
|
||||
}
|
||||
|
||||
# Redis
|
||||
backup_redis() {
|
||||
local type=${1:-rdb}
|
||||
log "開始 Redis 備份..."
|
||||
|
||||
redis-cli -a "$REDIS_PASSWORD" SAVE >/dev/null 2>&1
|
||||
cp /opt/homebrew/var/db/redis/dump.rdb "$BACKUP_ROOT/redis/redis_rdb_${TIMESTAMP}.rdb"
|
||||
sha256sum "$BACKUP_ROOT/redis/redis_rdb_${TIMESTAMP}.rdb" >"$BACKUP_ROOT/redis/redis_rdb_${TIMESTAMP}.sha256"
|
||||
|
||||
log_success "Redis: RDB 備份完成"
|
||||
}
|
||||
|
||||
# MariaDB (包含 WordPress)
|
||||
backup_mariadb() {
|
||||
local type=${1:-db}
|
||||
log "開始 MariaDB 備份..."
|
||||
|
||||
# 所有數據庫
|
||||
mysqldump -u "$MARIADB_USER" -p"$MARIADB_PASSWORD" --all-databases | gzip > \
|
||||
"$BACKUP_ROOT/mariadb/mariadb_db_all_${TIMESTAMP}.sql.gz"
|
||||
sha256sum "$BACKUP_ROOT/mariadb/mariadb_db_all_${TIMESTAMP}.sql.gz" >"$BACKUP_ROOT/mariadb/mariadb_db_${TIMESTAMP}.sha256"
|
||||
|
||||
# WordPress 數據庫
|
||||
mysqldump -u "$MARIADB_USER" -p"$MARIADB_PASSWORD" wordpress | gzip > \
|
||||
"$BACKUP_ROOT/mariadb/mariadb_db_wordpress_${TIMESTAMP}.sql.gz"
|
||||
sha256sum "$BACKUP_ROOT/mariadb/mariadb_db_wordpress_${TIMESTAMP}.sql.gz" >>"$BACKUP_ROOT/mariadb/mariadb_db_${TIMESTAMP}.sha256"
|
||||
|
||||
log_success "MariaDB: 數據庫備份完成 (包含 WordPress)"
|
||||
}
|
||||
|
||||
# WordPress 文件
|
||||
backup_wordpress_files() {
|
||||
local wordpress_dir="/Users/accusys/wordpress/web"
|
||||
local backup_dir="$BACKUP_ROOT/wordpress"
|
||||
|
||||
log "開始 WordPress 文件備份..."
|
||||
|
||||
# 確保備份目錄存在
|
||||
mkdir -p "$backup_dir"
|
||||
|
||||
# 排除不必要的目錄
|
||||
if [ -d "$wordpress_dir" ]; then
|
||||
tar --exclude='wp-content/cache/*' \
|
||||
--exclude='wp-content/uploads/cache/*' \
|
||||
--exclude='.git/*' \
|
||||
-czf "$backup_dir/wordpress_files_${TIMESTAMP}.tar.gz" \
|
||||
-C /Users/accusys/wordpress web/
|
||||
|
||||
sha256sum "$backup_dir/wordpress_files_${TIMESTAMP}.tar.gz" >>"$backup_dir/wordpress_${TIMESTAMP}.sha256" 2>/dev/null ||
|
||||
sha256sum "$backup_dir/wordpress_files_${TIMESTAMP}.tar.gz" >"$backup_dir/wordpress_${TIMESTAMP}.sha256"
|
||||
|
||||
log_success "WordPress: 文件備份完成"
|
||||
else
|
||||
log_error "WordPress 目錄不存在: $wordpress_dir"
|
||||
fi
|
||||
}
|
||||
|
||||
# n8n
|
||||
backup_n8n() {
|
||||
local type=${1:-full}
|
||||
log "開始 n8n 備份..."
|
||||
|
||||
# 數據庫
|
||||
PGPASSWORD="$PG_PASSWORD" pg_dump -U "$PG_USER" -d n8n | gzip >"$BACKUP_ROOT/n8n/n8n_db_${TIMESTAMP}.sql.gz"
|
||||
|
||||
# 數據目錄
|
||||
if [ -d "/Users/accusys/momentry/var/n8n" ]; then
|
||||
tar -czf "$BACKUP_ROOT/n8n/n8n_data_${TIMESTAMP}.tar.gz" -C /Users/accusys/momentry/var n8n/
|
||||
fi
|
||||
|
||||
# SHA256
|
||||
sha256sum "$BACKUP_ROOT/n8n"/n8n_* >"$BACKUP_ROOT/n8n/n8n_${TIMESTAMP}.sha256"
|
||||
|
||||
log_success "n8n: 完整備份完成"
|
||||
}
|
||||
|
||||
# Qdrant
|
||||
backup_qdrant() {
|
||||
local type=${1:-full}
|
||||
log "開始 Qdrant 備份..."
|
||||
|
||||
# 嘗試使用 Snapshots API
|
||||
COLLECTIONS=$(curl -s -H "api-key: $QDRANT_API_KEY" \
|
||||
http://localhost:6333/collections | jq -r '.result[].name' 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$COLLECTIONS" ] && [ "$COLLECTIONS" != "null" ]; then
|
||||
for COLLECTION in $COLLECTIONS; do
|
||||
curl -X POST -H "api-key: $QDRANT_API_KEY" \
|
||||
"http://localhost:6333/collections/${COLLECTION}/snapshots" \
|
||||
-o "$BACKUP_ROOT/qdrant/qdrant_snapshot_${COLLECTION}_${TIMESTAMP}.tar.gz" 2>/dev/null || true
|
||||
done
|
||||
else
|
||||
# 數據目錄備份
|
||||
tar -czf "$BACKUP_ROOT/qdrant/qdrant_data_${TIMESTAMP}.tar.gz" \
|
||||
-C /Users/accusys/momentry/var qdrant/ 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# SHA256
|
||||
sha256sum "$BACKUP_ROOT/qdrant"/qdrant_* >"$BACKUP_ROOT/qdrant/qdrant_${TIMESTAMP}.sha256"
|
||||
|
||||
log_success "Qdrant: 備份完成"
|
||||
}
|
||||
|
||||
# Gitea
|
||||
backup_gitea() {
|
||||
local type=${1:-full}
|
||||
log "開始 Gitea 備份..."
|
||||
|
||||
# 數據目錄
|
||||
if [ -d "/Users/accusys/momentry/var/gitea" ]; then
|
||||
tar -czf "$BACKUP_ROOT/gitea/gitea_data_${TIMESTAMP}.tar.gz" \
|
||||
-C /Users/accusys/momentry/var gitea/
|
||||
fi
|
||||
|
||||
# 配置目錄
|
||||
if [ -d "/Users/accusys/momentry/etc/gitea" ]; then
|
||||
tar -czf "$BACKUP_ROOT/gitea/gitea_cfg_${TIMESTAMP}.tar.gz" \
|
||||
-C /Users/accusys/momentry/etc gitea/
|
||||
fi
|
||||
|
||||
# SHA256
|
||||
sha256sum "$BACKUP_ROOT/gitea"/gitea_* >"$BACKUP_ROOT/gitea/gitea_${TIMESTAMP}.sha256"
|
||||
|
||||
log_success "Gitea: 完整備份完成"
|
||||
}
|
||||
|
||||
# Ollama
|
||||
backup_ollama() {
|
||||
local type=${1:-cfg}
|
||||
log "開始 Ollama 備份..."
|
||||
|
||||
# 配置目錄
|
||||
if [ -d "/Users/accusys/momentry/etc/ollama" ]; then
|
||||
tar -czf "$BACKUP_ROOT/ollama/ollama_cfg_${TIMESTAMP}.tar.gz" \
|
||||
-C /Users/accusys/momentry/etc ollama/
|
||||
fi
|
||||
|
||||
# 環境變數
|
||||
if [ -f "/Users/accusys/momentry/var/ollama/environment.txt" ]; then
|
||||
cp /Users/accusys/momentry/var/ollama/environment.txt "$BACKUP_ROOT/ollama/ollama_env_${TIMESTAMP}.txt"
|
||||
fi
|
||||
|
||||
# SHA256
|
||||
sha256sum "$BACKUP_ROOT/ollama"/ollama_* >"$BACKUP_ROOT/ollama/ollama_${TIMESTAMP}.sha256"
|
||||
|
||||
log_success "Ollama: 配置備份完成"
|
||||
}
|
||||
|
||||
# Caddy
|
||||
backup_caddy() {
|
||||
local type=${1:-cfg}
|
||||
log "開始 Caddy 備份..."
|
||||
|
||||
# 配置
|
||||
if [ -f "/Users/accusys/momentry/etc/Caddyfile" ]; then
|
||||
tar -czf "$BACKUP_ROOT/caddy/caddy_cfg_${TIMESTAMP}.tar.gz" \
|
||||
-C /Users/accusys/momentry/etc Caddyfile
|
||||
fi
|
||||
|
||||
# SHA256
|
||||
sha256sum "$BACKUP_ROOT/caddy"/caddy_* >"$BACKUP_ROOT/caddy/caddy_${TIMESTAMP}.sha256"
|
||||
|
||||
log_success "Caddy: 配置備份完成"
|
||||
}
|
||||
|
||||
# SftpGo
|
||||
backup_sftpgo() {
|
||||
local type=${1:-cfg}
|
||||
log "開始 SftpGo 備份..."
|
||||
|
||||
# 配置
|
||||
if [ -d "/Users/accusys/momentry/etc/sftpgo" ]; then
|
||||
tar -czf "$BACKUP_ROOT/sftpgo/sftpgo_cfg_${TIMESTAMP}.tar.gz" \
|
||||
-C /Users/accusys/momentry/etc sftpgo/
|
||||
fi
|
||||
|
||||
# PostgreSQL 數據庫 (SFTPGo 已遷移到 PostgreSQL)
|
||||
PGPASSWORD="$SFTPGO_PASSWORD" pg_dump -U "$SFTPGO_USER" -h localhost -d sftpgo | gzip >"$BACKUP_ROOT/sftpgo/sftpgo_db_${TIMESTAMP}.sql.gz"
|
||||
|
||||
# SHA256
|
||||
sha256sum "$BACKUP_ROOT/sftpgo"/sftpgo_* >"$BACKUP_ROOT/sftpgo/sftpgo_${TIMESTAMP}.sha256"
|
||||
|
||||
log_success "SftpGo: 配置和數據庫備份完成"
|
||||
}
|
||||
|
||||
# MongoDB
|
||||
backup_mongodb() {
|
||||
local type=${1:-full}
|
||||
log "開始 MongoDB 備份..."
|
||||
|
||||
# 使用 mongodump 備份 (避免文件鎖問題)
|
||||
local MONGO_BACKUP_DIR="/tmp/mongodb_backup_${TIMESTAMP}"
|
||||
mkdir -p "$MONGO_BACKUP_DIR"
|
||||
|
||||
# mongodump 需要認證
|
||||
if [ -n "$MONGODB_PASSWORD" ]; then
|
||||
mongodump --uri="mongodb://localhost:27017" \
|
||||
--username="$MONGODB_USER" \
|
||||
--password="$MONGODB_PASSWORD" \
|
||||
--authenticationDatabase=admin \
|
||||
--out="$MONGO_BACKUP_DIR" 2>/dev/null || true
|
||||
else
|
||||
mongodump --uri="mongodb://localhost:27017" \
|
||||
--out="$MONGO_BACKUP_DIR" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 打包
|
||||
if [ -d "$MONGO_BACKUP_DIR" ] && [ "$(ls -A $MONGO_BACKUP_DIR 2>/dev/null)" ]; then
|
||||
tar -czf "$BACKUP_ROOT/mongodb/mongodb_data_${TIMESTAMP}.tar.gz" \
|
||||
-C "$MONGO_BACKUP_DIR" .
|
||||
rm -rf "$MONGO_BACKUP_DIR"
|
||||
log "MongoDB: mongodump 備份完成"
|
||||
else
|
||||
log_warn "MongoDB: mongodump 備份失敗或數據庫為空"
|
||||
rm -rf "$MONGO_BACKUP_DIR"
|
||||
fi
|
||||
|
||||
# SHA256
|
||||
sha256sum "$BACKUP_ROOT/mongodb"/mongodb_* >"$BACKUP_ROOT/mongodb/mongodb_${TIMESTAMP}.sha256"
|
||||
|
||||
log_success "MongoDB: 備份完成"
|
||||
}
|
||||
|
||||
# PHP
|
||||
backup_php() {
|
||||
local type=${1:-cfg}
|
||||
log "開始 PHP 備份..."
|
||||
|
||||
# 配置
|
||||
if [ -d "/Users/accusys/momentry/etc/php/8.5" ]; then
|
||||
tar -czf "$BACKUP_ROOT/php/php_cfg_${TIMESTAMP}.tar.gz" \
|
||||
-C /Users/accusys/momentry/etc php/8.5
|
||||
fi
|
||||
|
||||
# SHA256
|
||||
sha256sum "$BACKUP_ROOT/php"/php_* >"$BACKUP_ROOT/php/php_${TIMESTAMP}.sha256"
|
||||
|
||||
log_success "PHP: 配置備份完成"
|
||||
}
|
||||
|
||||
# Momentry Output 目錄 (v2 新增)
|
||||
backup_momentry_output() {
|
||||
local type=${1:-data}
|
||||
log "開始 Momentry Output 備份..."
|
||||
|
||||
# Output 目錄
|
||||
local OUTPUT_DIR="/Users/accusys/momentry/output"
|
||||
|
||||
if [ -d "$OUTPUT_DIR" ]; then
|
||||
tar -czf "$BACKUP_ROOT/momentry/momentry_output_${TIMESTAMP}.tar.gz" \
|
||||
-C /Users/accusys/momentry output/
|
||||
log "Momentry Output: 備份 $OUTPUT_DIR"
|
||||
else
|
||||
log_warn "Momentry Output: 目錄不存在或為空 ($OUTPUT_DIR)"
|
||||
fi
|
||||
|
||||
# SHA256
|
||||
sha256sum "$BACKUP_ROOT/momentry"/momentry_output_* >"$BACKUP_ROOT/momentry/momentry_output_${TIMESTAMP}.sha256" 2>/dev/null || true
|
||||
|
||||
log_success "Momentry Output: 備份完成"
|
||||
}
|
||||
|
||||
#===============================================================================
|
||||
# 恢復函數
|
||||
#===============================================================================
|
||||
|
||||
restore_postgresql() {
|
||||
local timestamp=$1
|
||||
log "恢復 PostgreSQL..."
|
||||
|
||||
# 找到對應的備份文件
|
||||
local backup_file=$(ls "$BACKUP_ROOT/postgresql"/postgresql_db_momentry_${timestamp}.sql.gz 2>/dev/null | head -1)
|
||||
|
||||
if [ -n "$backup_file" ]; then
|
||||
gunzip -c "$backup_file" | PGPASSWORD="$PG_PASSWORD" psql -U "$PG_USER" -d momentry
|
||||
log_success "PostgreSQL 恢復完成"
|
||||
else
|
||||
log_error "找不到 PostgreSQL 備份文件: $timestamp"
|
||||
fi
|
||||
}
|
||||
|
||||
restore_redis() {
|
||||
local timestamp=$1
|
||||
log "恢復 Redis..."
|
||||
|
||||
local backup_file=$(ls "$BACKUP_ROOT/redis"/redis_rdb_${timestamp}.rdb 2>/dev/null | head -1)
|
||||
|
||||
if [ -n "$backup_file" ]; then
|
||||
redis-cli -a "$REDIS_PASSWORD" SHUTDOWN 2>/dev/null || true
|
||||
cp "$backup_file" /opt/homebrew/var/db/redis/dump.rdb
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.redis.plist 2>/dev/null ||
|
||||
redis-server --daemonize yes --requirepass "$REDIS_PASSWORD"
|
||||
log_success "Redis 恢復完成"
|
||||
else
|
||||
log_error "找不到 Redis 備份文件: $timestamp"
|
||||
fi
|
||||
}
|
||||
|
||||
restore_mariadb() {
|
||||
local timestamp=$1
|
||||
log "恢復 MariaDB (包含 WordPress)..."
|
||||
|
||||
local backup_file=$(ls "$BACKUP_ROOT/mariadb"/mariadb_db_wordpress_${timestamp}.sql.gz 2>/dev/null | head -1)
|
||||
|
||||
if [ -n "$backup_file" ]; then
|
||||
gunzip -c "$backup_file" | mysql -u momentry_backup -pmomentry_backup_pwd_2026 wordpress
|
||||
log_success "MariaDB/WordPress 恢復完成"
|
||||
else
|
||||
log_error "找不到 MariaDB 備份文件: $timestamp"
|
||||
fi
|
||||
}
|
||||
|
||||
restore_n8n() {
|
||||
local timestamp=$1
|
||||
log "恢復 n8n..."
|
||||
|
||||
# 恢復數據庫
|
||||
local db_backup=$(ls "$BACKUP_ROOT/n8n"/n8n_db_${timestamp}.sql.gz 2>/dev/null | head -1)
|
||||
if [ -n "$db_backup" ]; then
|
||||
gunzip -c "$db_backup" | PGPASSWORD="$PG_PASSWORD" psql -U "$PG_USER" -d n8n
|
||||
fi
|
||||
|
||||
# 恢復數據目錄
|
||||
local data_backup=$(ls "$BACKUP_ROOT/n8n"/n8n_data_${timestamp}.tar.gz 2>/dev/null | head -1)
|
||||
if [ -n "$data_backup" ]; then
|
||||
rm -rf /Users/accusys/momentry/var/n8n
|
||||
tar -xzf "$data_backup" -C /Users/accusys/momentry/var/
|
||||
fi
|
||||
|
||||
log_success "n8n 恢復完成"
|
||||
}
|
||||
|
||||
restore_qdrant() {
|
||||
local timestamp=$1
|
||||
log "恢復 Qdrant..."
|
||||
|
||||
pkill qdrant 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
local data_backup=$(ls "$BACKUP_ROOT/qdrant"/qdrant_data_${timestamp}.tar.gz 2>/dev/null | head -1)
|
||||
if [ -n "$data_backup" ]; then
|
||||
rm -rf /Users/accusys/momentry/var/qdrant
|
||||
tar -xzf "$data_backup" -C /Users/accusys/momentry/var/
|
||||
fi
|
||||
|
||||
launchctl load /Library/LaunchDaemons/com.momentry.qdrant.plist 2>/dev/null || true
|
||||
log_success "Qdrant 恢復完成"
|
||||
}
|
||||
|
||||
restore_gitea() {
|
||||
local timestamp=$1
|
||||
log "恢復 Gitea..."
|
||||
|
||||
# 停止 Gitea
|
||||
pkill gitea 2>/dev/null || true
|
||||
|
||||
# 恢復數據
|
||||
local data_backup=$(ls "$BACKUP_ROOT/gitea"/gitea_data_${timestamp}.tar.gz 2>/dev/null | head -1)
|
||||
if [ -n "$data_backup" ]; then
|
||||
rm -rf /Users/accusys/momentry/var/gitea
|
||||
tar -xzf "$data_backup" -C /Users/accusys/momentry/var/
|
||||
fi
|
||||
|
||||
# 恢復配置
|
||||
local cfg_backup=$(ls "$BACKUP_ROOT/gitea"/gitea_cfg_${timestamp}.tar.gz 2>/dev/null | head -1)
|
||||
if [ -n "$cfg_backup" ]; then
|
||||
rm -rf /Users/accusys/momentry/etc/gitea
|
||||
tar -xzf "$cfg_backup" -C /Users/accusys/momentry/etc/
|
||||
fi
|
||||
|
||||
log_success "Gitea 恢復完成"
|
||||
}
|
||||
|
||||
restore_ollama() {
|
||||
local timestamp=$1
|
||||
log "恢復 Ollama..."
|
||||
|
||||
# 恢復配置
|
||||
local cfg_backup=$(ls "$BACKUP_ROOT/ollama"/ollama_cfg_${timestamp}.tar.gz 2>/dev/null | head -1)
|
||||
if [ -n "$cfg_backup" ]; then
|
||||
rm -rf /Users/accusys/momentry/etc/ollama
|
||||
tar -xzf "$cfg_backup" -C /Users/accusys/momentry/etc/
|
||||
fi
|
||||
|
||||
log_success "Ollama 恢復完成"
|
||||
}
|
||||
|
||||
restore_caddy() {
|
||||
local timestamp=$1
|
||||
log "恢復 Caddy..."
|
||||
|
||||
local cfg_backup=$(ls "$BACKUP_ROOT/caddy"/caddy_cfg_${timestamp}.tar.gz 2>/dev/null | head -1)
|
||||
if [ -n "$cfg_backup" ]; then
|
||||
tar -xzf "$cfg_backup" -C /Users/accusys/momentry/etc/
|
||||
caddy reload --config /Users/accusys/momentry/etc/Caddyfile
|
||||
fi
|
||||
|
||||
log_success "Caddy 恢復完成"
|
||||
}
|
||||
|
||||
restore_sftpgo() {
|
||||
local timestamp=$1
|
||||
log "恢復 SftpGo..."
|
||||
|
||||
# 停止 SFTPGo
|
||||
pkill -f sftpgo || true
|
||||
sleep 2
|
||||
|
||||
# 恢復配置
|
||||
local cfg_backup=$(ls "$BACKUP_ROOT/sftpgo"/sftpgo_cfg_${timestamp}.tar.gz 2>/dev/null | head -1)
|
||||
if [ -n "$cfg_backup" ]; then
|
||||
rm -rf /Users/accusys/momentry/etc/sftpgo
|
||||
tar -xzf "$cfg_backup" -C /Users/accusys/momentry/etc/
|
||||
fi
|
||||
|
||||
# 恢復 PostgreSQL 數據庫
|
||||
local db_backup=$(ls "$BACKUP_ROOT/sftpgo"/sftpgo_db_${timestamp}.sql.gz 2>/dev/null | head -1)
|
||||
if [ -n "$db_backup" ]; then
|
||||
# 確保數據庫存在
|
||||
PGPASSWORD="$PG_PASSWORD" psql -U "$PG_USER" -h localhost -d postgres -c "DROP DATABASE IF EXISTS sftpgo;" 2>/dev/null
|
||||
PGPASSWORD="$PG_PASSWORD" psql -U "$PG_USER" -h localhost -d postgres -c "CREATE DATABASE sftpgo OWNER $SFTPGO_USER;" 2>/dev/null
|
||||
gunzip -c "$db_backup" | PGPASSWORD="$SFTPGO_PASSWORD" psql -U "$SFTPGO_USER" -h localhost -d sftpgo 2>/dev/null
|
||||
fi
|
||||
|
||||
# 重啟 SFTPGo
|
||||
cd /Users/accusys/momentry/var/sftpgo
|
||||
/opt/homebrew/opt/sftpgo/bin/sftpgo serve --config-file /Users/accusys/momentry/etc/sftpgo/sftpgo.json &
|
||||
|
||||
log_success "SftpGo 恢復完成"
|
||||
}
|
||||
|
||||
restore_mongodb() {
|
||||
local timestamp=$1
|
||||
log "恢復 MongoDB..."
|
||||
|
||||
# 解壓縮到臨時目錄
|
||||
local MONGO_RESTORE_DIR="/tmp/mongodb_restore_${timestamp}"
|
||||
mkdir -p "$MONGO_RESTORE_DIR"
|
||||
|
||||
local data_backup=$(ls "$BACKUP_ROOT/mongodb"/mongodb_data_${timestamp}.tar.gz 2>/dev/null | head -1)
|
||||
if [ -n "$data_backup" ]; then
|
||||
tar -xzf "$data_backup" -C "$MONGO_RESTORE_DIR/"
|
||||
|
||||
# 使用 mongorestore 恢復
|
||||
if [ -n "$MONGODB_PASSWORD" ]; then
|
||||
mongorestore --uri="mongodb://localhost:27017" \
|
||||
--username="$MONGODB_USER" \
|
||||
--password="$MONGODB_PASSWORD" \
|
||||
--authenticationDatabase=admin \
|
||||
--drop \
|
||||
--dir="$MONGO_RESTORE_DIR" 2>/dev/null || true
|
||||
else
|
||||
mongorestore --uri="mongodb://localhost:27017" \
|
||||
--drop \
|
||||
--dir="$MONGO_RESTORE_DIR" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
rm -rf "$MONGO_RESTORE_DIR"
|
||||
else
|
||||
log_warn "MongoDB: 未找到備份文件"
|
||||
fi
|
||||
|
||||
log_success "MongoDB 恢復完成"
|
||||
}
|
||||
|
||||
restore_php() {
|
||||
local timestamp=$1
|
||||
log "恢復 PHP..."
|
||||
|
||||
local cfg_backup=$(ls "$BACKUP_ROOT/php"/php_cfg_${timestamp}.tar.gz 2>/dev/null | head -1)
|
||||
if [ -n "$cfg_backup" ]; then
|
||||
rm -rf /Users/accusys/momentry/etc/php/8.5
|
||||
tar -xzf "$cfg_backup" -C /Users/accusys/momentry/etc/php/
|
||||
fi
|
||||
|
||||
log_success "PHP 恢復完成"
|
||||
}
|
||||
|
||||
restore_momentry_output() {
|
||||
local timestamp=$1
|
||||
log "恢復 Momentry Output..."
|
||||
|
||||
# v2: Output 目錄可能有多個版本,嘗試 v2 版本再回退到舊版本
|
||||
local output_backup=""
|
||||
|
||||
# 嘗試 v2 版本
|
||||
output_backup=$(ls "$BACKUP_ROOT/momentry"/momentry_output_v2_${timestamp}.tar.gz 2>/dev/null | head -1)
|
||||
|
||||
# 如果沒有 v2 版本,嘗試舊格式
|
||||
if [ -z "$output_backup" ]; then
|
||||
output_backup=$(ls "$BACKUP_ROOT/momentry"/momentry_output_${timestamp}.tar.gz 2>/dev/null | head -1)
|
||||
fi
|
||||
|
||||
if [ -n "$output_backup" ]; then
|
||||
rm -rf /Users/accusys/momentry/output
|
||||
mkdir -p /Users/accusys/momentry
|
||||
tar -xzf "$output_backup" -C /Users/accusys/momentry/
|
||||
log "Momentry Output: 恢復 $(basename $output_backup)"
|
||||
else
|
||||
log_warn "Momentry Output: 未找到備份檔案"
|
||||
fi
|
||||
|
||||
log_success "Momentry Output 恢復完成"
|
||||
}
|
||||
|
||||
#===============================================================================
|
||||
# 主程序
|
||||
#===============================================================================
|
||||
|
||||
main() {
|
||||
local command=${1:-all}
|
||||
local service=${2:-}
|
||||
local type=${3:-}
|
||||
|
||||
# 確保日誌目錄存在
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
echo ""
|
||||
log "=========================================="
|
||||
log "Momentry 備份系統"
|
||||
log "時間戳: $TIMESTAMP"
|
||||
log "=========================================="
|
||||
|
||||
case $command in
|
||||
restore | rollback)
|
||||
if [ -z "$service" ]; then
|
||||
log_error "請指定恢復時間戳 (YYYYMMDD_HHMMSS 或 v2_YYYYMMDD_HHMMSS)"
|
||||
echo "示例: $0 restore v2_20260325_030000"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "開始恢復到斷點: $service"
|
||||
|
||||
for svc in "${SERVICES[@]}"; do
|
||||
case $svc in
|
||||
postgresql) restore_postgresql "$service" ;;
|
||||
redis) restore_redis "$service" ;;
|
||||
mariadb) restore_mariadb "$service" ;;
|
||||
n8n) restore_n8n "$service" ;;
|
||||
qdrant) restore_qdrant "$service" ;;
|
||||
gitea) restore_gitea "$service" ;;
|
||||
ollama) restore_ollama "$service" ;;
|
||||
caddy) restore_caddy "$service" ;;
|
||||
sftpgo) restore_sftpgo "$service" ;;
|
||||
mongodb) restore_mongodb "$service" ;;
|
||||
php) restore_php "$service" ;;
|
||||
momentry_output) restore_momentry_output "$service" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
log "=========================================="
|
||||
log_success "恢復完成!"
|
||||
log "=========================================="
|
||||
;;
|
||||
|
||||
list)
|
||||
log "可用時間點:"
|
||||
for dir in "$BACKUP_ROOT"/*/; do
|
||||
local svc=$(basename "$dir")
|
||||
echo " $svc:"
|
||||
ls -1 "$dir"*.tar.gz "$dir"*.sql.gz "$dir"*.rdb 2>/dev/null |
|
||||
sed 's/.*\([0-9]\{8\}\_[0-9]\{6\}\).*/\1/' | sort -u | sed 's/^/ /'
|
||||
done
|
||||
;;
|
||||
|
||||
status)
|
||||
log "備份狀態:"
|
||||
echo ""
|
||||
for svc in "${SERVICES[@]}"; do
|
||||
local date_part="${TIMESTAMP#*_}" # Remove v2_ prefix
|
||||
date_part="${date_part:0:8}" # Extract YYYYMMDD
|
||||
local latest=$(find "$BACKUP_ROOT/$svc" \( -name "*_${date_part}_*" -o -name "*_v2_${date_part}_*" \) -type f 2>/dev/null | head -1)
|
||||
if [ -n "$latest" ]; then
|
||||
local size=$(du -h "$latest" | cut -f1)
|
||||
echo -e " $svc: ${GREEN}✓${NC} $size"
|
||||
else
|
||||
echo -e " $svc: ${RED}✗${NC}"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
|
||||
all)
|
||||
# 備份所有服務
|
||||
for svc in "${SERVICES[@]}"; do
|
||||
case $svc in
|
||||
postgresql) backup_postgresql "$type" ;;
|
||||
redis) backup_redis "$type" ;;
|
||||
mariadb) backup_mariadb "$type" ;;
|
||||
wordpress) backup_wordpress_files ;;
|
||||
n8n) backup_n8n "$type" ;;
|
||||
qdrant) backup_qdrant "$type" ;;
|
||||
gitea) backup_gitea "$type" ;;
|
||||
ollama) backup_ollama "$type" ;;
|
||||
caddy) backup_caddy "$type" ;;
|
||||
sftpgo) backup_sftpgo "$type" ;;
|
||||
mongodb) backup_mongodb "$type" ;;
|
||||
php) backup_php "$type" ;;
|
||||
momentry_output) backup_momentry_output "$type" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
log "=========================================="
|
||||
log_success "所有備份完成! 時間戳: $TIMESTAMP"
|
||||
log "=========================================="
|
||||
;;
|
||||
|
||||
*)
|
||||
# 備份特定服務
|
||||
if [ -n "$service" ]; then
|
||||
case $service in
|
||||
postgresql) backup_postgresql "$type" ;;
|
||||
redis) backup_redis "$type" ;;
|
||||
mariadb) backup_mariadb "$type" ;;
|
||||
wordpress) backup_wordpress_files ;;
|
||||
n8n) backup_n8n "$type" ;;
|
||||
qdrant) backup_qdrant "$type" ;;
|
||||
gitea) backup_gitea "$type" ;;
|
||||
ollama) backup_ollama "$type" ;;
|
||||
caddy) backup_caddy "$type" ;;
|
||||
sftpgo) backup_sftpgo "$type" ;;
|
||||
mongodb) backup_mongodb "$type" ;;
|
||||
php) backup_php "$type" ;;
|
||||
momentry_output) backup_momentry_output "$type" ;;
|
||||
*)
|
||||
log_error "未知服務: $service"
|
||||
echo "可用服務: ${SERVICES[*]}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
else
|
||||
log_error "請指定命令或服務"
|
||||
echo "用法: $0 [命令] [服務] [類型]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " all - 備份所有服務 (默認)"
|
||||
echo " <service> - 備份特定服務"
|
||||
echo " restore - 恢復到指定斷點"
|
||||
echo " list - 列出可用時間點"
|
||||
echo " status - 顯示備份狀態"
|
||||
echo ""
|
||||
echo "服務: ${SERVICES[*]}"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user