#!/bin/bash # ============================================================ # dual-backup.sh — 双备份同步脚本 # 本机 git 版本控制 + 服务器 SMB 远程备份 # 用法: dual-backup.sh [push|pull|status] # push : 本机 → 服务器(默认,增量同步) # pull : 服务器 → 本机(恢复场景) # status: 查看备份状态 # ============================================================ set -e HOME_DIR="$HOME" HERMES="$HOME_DIR/.hermes" BACKUP_ROOT="/mnt/server-backup/xiaowei" BACKUP_CONFIG="$BACKUP_ROOT/config" BACKUP_SCRIPTS="$BACKUP_ROOT/scripts" BACKUP_WORKFLOWS="$BACKUP_ROOT/workflows" BACKUP_DAEMON="$BACKUP_ROOT/daemon" BACKUP_SYSTEMD="$BACKUP_ROOT/systemd" BACKUP_SNAPSHOTS="$BACKUP_ROOT/snapshots" LOG="$HERMES/watchdog/dual-backup.log" mkdir -p "$(dirname "$LOG")" log() { echo "[$(date '+%H:%M:%S')] $*" | tee -a "$LOG"; } # 检查服务器挂载 check_mount() { if ! mountpoint -q /mnt/server-backup; then log "⚠️ 服务器未挂载,尝试连接..." sudo mount -t cifs //192.168.123.11/beifen /mnt/server-backup \ -o username=administrator,password=xue.2538,uid=1000,gid=1000,iocharset=utf8,file_mode=0755,dir_mode=0755 2>/dev/null sleep 2 if ! mountpoint -q /mnt/server-backup; then log "❌ 服务器挂载失败" return 1 fi fi return 0 } # === 推送到服务器 === push_backup() { log "📤 开始双备份推送..." if ! check_mount; then log "❌ 跳过备份(服务器离线)" return 1 fi # 1. 本机 git 快照(确保变更已记录) log " 本机快照..." bash "$HERMES/scripts/config-protector.sh snapshot" 2>/dev/null || true # 2. 同步核心文档 log " 同步配置..." rsync -av --delete "$HERMES/SOUL.md" "$BACKUP_CONFIG/" 2>&1 | tail -1 rsync -av --delete "$HERMES/AGENTS.md" "$BACKUP_CONFIG/" 2>&1 | tail -1 rsync -av --delete "$HERMES/MEMORY.md" "$BACKUP_CONFIG/" 2>&1 | tail -1 rsync -av --delete "$HERMES/USER.md" "$BACKUP_CONFIG/" 2>&1 | tail -1 rsync -av --delete "$HERMES/CREATIVE.md" "$BACKUP_CONFIG/" 2>&1 | tail -1 # 3. 同步核心脚本 log " 同步脚本..." rsync -av --delete "$HERMES/scripts/"*.py "$BACKUP_SCRIPTS/" 2>&1 | tail -1 rsync -av --delete "$HERMES/scripts/"*.sh "$BACKUP_SCRIPTS/" 2>&1 | tail -1 # 4. 同步工作流模板 log " 同步工作流..." rsync -av --delete "$HERMES/workflows/"*.yaml "$BACKUP_WORKFLOWS/" 2>&1 | tail -1 # 5. 同步 daemon 状态 log " 同步 daemon..." rsync -av --delete "$HERMES/daemon/solutions.json" "$BACKUP_DAEMON/" 2>&1 | tail -1 rsync -av --delete "$HERMES/daemon/context.json" "$BACKUP_DAEMON/" 2>&1 | tail -1 # 6. 同步 systemd 服务文件 log " 同步 systemd..." rsync -av --delete "$HOME_DIR/.config/systemd/user/xiaowei-daemon.service" "$BACKUP_SYSTEMD/" 2>&1 | tail -1 rsync -av --delete "$HOME_DIR/.config/systemd/user/xiaowei-startup.service" "$BACKUP_SYSTEMD/" 2>&1 | tail -1 # 7. 同步 skills 目录(通过 git bundle,包含全部变更历史) log " 同步 skills..." if [ -d "$HERMES/.git" ]; then cd "$HERMES" git bundle create "$BACKUP_SNAPSHOTS/hermes-git-$(date '+%Y%m%d').bundle" --all 2>/dev/null || true fi # 8. 同步 cron 配置 log " 同步 cron..." if [ -f "$HERMES/cron/jobs.json" ]; then cp "$HERMES/cron/jobs.json" "$BACKUP_ROOT/cron-jobs.json" log " cron配置已备份" else log " ⚠️ 无cron配置可备份" fi # 9. 同步织忆数据(LanceDB + SQLite图谱 + 真实数据) log " 同步织忆数据..." # 9a. LanceDB 数据 if [ -d "/var/lib/memoryweave" ]; then tar czf "$BACKUP_SNAPSHOTS/zhiyi-lancedb-$(date '+%Y%m%d').tar.gz" \ -C /var/lib/memoryweave --exclude='*.lance' --exclude='lancedb.bak' \ . 2>/dev/null || log " ⚠️ 织忆LanceDB同步失败" fi # 9b. SQLite 真实数据(绕过软链接,直接从 ~/.hermes/ 取) if [ -f "$HERMES/graph.db" ] || [ -f "$HERMES/memoryweave.db" ]; then tar czf "$BACKUP_SNAPSHOTS/zhiyi-sqlite-$(date '+%Y%m%d').tar.gz" \ -C "$HERMES" graph.db memoryweave.db 2>/dev/null || log " ⚠️ 织忆SQLite同步失败" log " 📦 织忆SQLite已备份" fi # 10. 定期快照(每天保留一份关键配置快照) SNAPSHOT_NAME="snapshot-$(date '+%Y%m%d')" if [ ! -d "$BACKUP_SNAPSHOTS/$SNAPSHOT_NAME" ]; then mkdir -p "$BACKUP_SNAPSHOTS/$SNAPSHOT_NAME" cp "$HERMES/SOUL.md" "$BACKUP_SNAPSHOTS/$SNAPSHOT_NAME/" cp "$HERMES/daemon/solutions.json" "$BACKUP_SNAPSHOTS/$SNAPSHOT_NAME/" 2>/dev/null || true log " 📸 每日快照: $SNAPSHOT_NAME" fi log "✅ 双备份推送完成" } # === 从服务器恢复 === pull_backup() { log "📥 从服务器恢复..." if ! check_mount; then log "❌ 跳过恢复(服务器离线)" return 1 fi # 检查备份是否存在 if [ ! -f "$BACKUP_CONFIG/SOUL.md" ]; then log "❌ 服务器上无备份数据" return 1 fi # 反向同步(服务器 → 本机) rsync -av --delete "$BACKUP_CONFIG/" "$HERMES/" 2>&1 | tail -1 rsync -av --delete "$BACKUP_SCRIPTS/" "$HERMES/scripts/" 2>&1 | tail -1 rsync -av --delete "$BACKUP_WORKFLOWS/" "$HERMES/workflows/" 2>&1 | tail -1 rsync -av "$BACKUP_DAEMON/solutions.json" "$HERMES/daemon/" 2>&1 | tail -1 log "✅ 恢复完成" } # === 查看状态 === show_status() { echo "=== 双备份状态 ===" echo "" if mountpoint -q /mnt/server-backup; then echo "🟢 服务器: 已连接" echo " 路径: /mnt/server-backup/xiaowei" echo " 空间: $(df -h /mnt/server-backup | awk 'NR==2 {print $3 "已用 / " $2 " (" $5 ")"}')" echo "" echo " 备份内容:" for dir in config scripts workflows daemon systemd snapshots; do count=$(find "/mnt/server-backup/xiaowei/$dir" -maxdepth 1 -type f 2>/dev/null | wc -l) last=$(ls -lt "/mnt/server-backup/xiaowei/$dir" 2>/dev/null | head -2 | tail -1 | awk '{print $6, $7, $8}') echo " $dir: $count 个文件 (最新: ${last:-无})" done else echo "🔴 服务器: 未连接" fi echo "" echo "=== 本机 git 状态 ===" cd "$HERMES" echo " 最近提交: $(git log --oneline -1 2>/dev/null || echo '无')" echo " 未跟踪: $(git status --short 2>/dev/null | wc -l) 个" echo " 稳定标记: $(git tag -l 'stable-*' 2>/dev/null | wc -l) 个" echo "" echo "=== Gitea ===" curl -sf --max-time 3 http://192.168.123.11:3000/api/v1/repos/xiaoxue_admin/xiaowei-system 2>/dev/null \ | python3 -c "import json,sys; d=json.load(sys.stdin); print(f' 仓库: {d.get(\"name\",\"?\")} | 更新: {d.get(\"updated_at\",\"?\")[:10]}')" 2>/dev/null \ || echo " 仓库: xiaowei-system (离线)" } # === 主入口 === case "${1:-push}" in push) push_backup ;; pull) pull_backup ;; status) show_status ;; *) echo "用法: dual-backup.sh [push|pull|status]" ;; esac