From ab17a350b4e2c2c7a4c2bf54b5a625a7939a50eb Mon Sep 17 00:00:00 2001 From: mango Date: Mon, 2 Feb 2026 10:45:49 +0800 Subject: [PATCH] =?UTF-8?q?v3.1:=20=E6=B7=BB=E5=8A=A0=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E6=81=A2=E5=A4=8D=E3=80=81=E8=BF=9C=E7=A8=8B=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E3=80=81=E5=90=8C=E6=AD=A5=E5=88=B0=E8=BF=9C=E7=A8=8B=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vps-snapshot.sh | 151 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 137 insertions(+), 14 deletions(-) diff --git a/vps-snapshot.sh b/vps-snapshot.sh index 57dc1f1..7c1bf57 100755 --- a/vps-snapshot.sh +++ b/vps-snapshot.sh @@ -501,6 +501,123 @@ create_snapshot() { echo "$snapshot_file" } +#=============================================================================== +# 本地恢复 +#=============================================================================== + +do_restore_local() { + load_config 2>/dev/null || true + local snap_dir="${LOCAL_DIR:-/var/snapshots}" + + echo "" + info "📂 本地快照列表:" + ls -lh "$snap_dir"/*.tar.gz 2>/dev/null || { error "无快照"; return 1; } + echo "" + read -p "输入快照文件名: " snap_file + + [ ! -f "$snap_dir/$snap_file" ] && { error "文件不存在"; return 1; } + + log "🔄 恢复快照: $snap_file" + + # 解压到临时目录 + local tmp_dir="/tmp/restore_$$" + mkdir -p "$tmp_dir" + tar -xzf "$snap_dir/$snap_file" -C "$tmp_dir" + + # 导入Docker + if [ -f "$tmp_dir/docker-images.tar.gz" ]; then + log "导入 Docker 镜像..." + gunzip -c "$tmp_dir/docker-images.tar.gz" | docker load + fi + + # 恢复应用数据 + if ls "$tmp_dir"/app-data_*.tar.gz &>/dev/null; then + log "恢复应用数据..." + tar -xzf "$tmp_dir"/app-data_*.tar.gz -C / 2>/dev/null || true + fi + + rm -rf "$tmp_dir" + log "✅ 恢复完成" +} + +#=============================================================================== +# 远程恢复 +#=============================================================================== + +do_restore_remote() { + echo "" + info "📡 从远程服务器拉取快照" + read -p "远程服务器 IP: " remote_ip + read -p "远程端口 [22]: " remote_port + remote_port=${remote_port:-22} + read -p "远程用户 [root]: " remote_user + remote_user=${remote_user:-root} + read -s -p "远程密码: " remote_pass + echo "" + read -p "远程快照路径 (如 /var/snapshots/xxx.tar.gz): " remote_path + + [ -z "$remote_ip" ] || [ -z "$remote_path" ] && { error "参数不完整"; return 1; } + + log "下载快照..." + local local_file="/tmp/remote_snapshot_$$.tar.gz" + sshpass -p "$remote_pass" scp -o StrictHostKeyChecking=no \ + -P "$remote_port" "$remote_user@$remote_ip:$remote_path" "$local_file" + + [ ! -f "$local_file" ] && { error "下载失败"; return 1; } + + log "🔄 恢复快照..." + local tmp_dir="/tmp/restore_$$" + mkdir -p "$tmp_dir" + tar -xzf "$local_file" -C "$tmp_dir" + + # 导入Docker + if [ -f "$tmp_dir/docker-images.tar.gz" ]; then + log "导入 Docker 镜像..." + gunzip -c "$tmp_dir/docker-images.tar.gz" | docker load + fi + + # 恢复应用数据 + if ls "$tmp_dir"/app-data_*.tar.gz &>/dev/null; then + log "恢复应用数据..." + tar -xzf "$tmp_dir"/app-data_*.tar.gz -C / 2>/dev/null || true + fi + + rm -rf "$tmp_dir" "$local_file" + log "✅ 恢复完成" +} + +#=============================================================================== +# 同步到远程 +#=============================================================================== + +do_sync_remote() { + load_config 2>/dev/null || true + + if [ -z "$REMOTE_IP" ]; then + error "未配置远程服务器,请先运行配置" + return 1 + fi + + local snap_dir="${LOCAL_DIR:-/var/snapshots}" + local latest=$(ls -t "$snap_dir"/*.tar.gz 2>/dev/null | head -1) + + [ -z "$latest" ] && { error "无本地快照"; return 1; } + + log "📤 同步到远程: $REMOTE_IP" + + # 创建远程目录 + sshpass -p "$REMOTE_PASS" ssh -o StrictHostKeyChecking=no \ + -p "${REMOTE_PORT:-22}" "${REMOTE_USER:-root}@$REMOTE_IP" \ + "mkdir -p ${REMOTE_DIR:-/backup}" + + # 同步 + sshpass -p "$REMOTE_PASS" rsync -avz --progress \ + -e "ssh -o StrictHostKeyChecking=no -p ${REMOTE_PORT:-22}" \ + "$latest" "${REMOTE_USER:-root}@$REMOTE_IP:${REMOTE_DIR:-/backup}/" + + log "✅ 同步完成" +} + #=============================================================================== # Telegram 通知 #=============================================================================== @@ -582,37 +699,43 @@ show_menu() { echo " 1) 首次配置 / 重新配置" echo " 2) 扫描已安装应用" echo " 3) 创建快照备份" - echo " 4) 一键迁移到新服务器" - echo " 5) 导出 Docker 数据" - echo " 6) 导入 Docker 数据" - echo " 7) 查看本地快照" - echo " 8) 安装依赖" + echo " 4) 从本地快照恢复" + echo " 5) 从远程服务器恢复" + echo " 6) 一键迁移到新服务器" + echo " 7) 导出 Docker 数据" + echo " 8) 导入 Docker 数据" + echo " 9) 查看本地快照" + echo " 10) 同步到远程" + echo " 11) 安装依赖" echo " 0) 退出" echo "" - read -p "请选择 [0-8]: " choice + read -p "请选择 [0-11]: " choice case $choice in 1) do_setup ;; 2) detect_apps ;; 3) - load_config - create_snapshot "$LOCAL_DIR" "$VPS_NAME" + load_config 2>/dev/null || true + create_snapshot "${LOCAL_DIR:-/var/snapshots}" "${VPS_NAME:-snapshot}" ;; - 4) do_migrate ;; - 5) + 4) do_restore_local ;; + 5) do_restore_remote ;; + 6) do_migrate ;; + 7) read -p "输出目录 [/var/snapshots]: " dir docker_export "${dir:-/var/snapshots}" ;; - 6) + 8) read -p "输入目录 [/var/snapshots]: " dir docker_import "${dir:-/var/snapshots}" ;; - 7) - load_config + 9) + load_config 2>/dev/null || true echo "" ls -lh "${LOCAL_DIR:-/var/snapshots}" 2>/dev/null || echo "无快照" ;; - 8) install_deps ;; + 10) do_sync_remote ;; + 11) install_deps ;; 0) exit 0 ;; *) error "无效选项" ;; esac