flnxi-Wu/scripts/backup
WuYongJie f646b71813
Some checks failed
CI Check / check (push) Has been cancelled
chore: 初始化本地仓库并同步最近改动
2025-11-18 17:50:16 +08:00

203 lines
6.6 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
import subprocess
import sys
import time
import os
# 服务器配置
SERVER_HOST = "45.249.208.118"
SERVER_USER = "root"
SERVER_PASSWORD = "lxvhNYFZ4160"
SERVER_PROJECT_DIR = "/root/youyidian-3d"
LOCAL_PACKAGE = "project-deploy.tar.gz"
def print_step(step, message):
print(f"\n{'='*60}")
print(f"步骤 {step}: {message}")
print('='*60)
def run_ssh_command(command):
"""执行SSH命令"""
full_cmd = f'ssh {SERVER_USER}@{SERVER_HOST} "{command}"'
print(f"\n执行命令: {command}\n")
# 使用 plink 或 ssh (需要手动输入密码)
result = subprocess.run(full_cmd, shell=True, capture_output=False, text=True)
return result.returncode == 0
def main():
print("\n" + "="*60)
print(" 由壹点3D素材系统 - 自动部署脚本")
print(f" 目标服务器: {SERVER_HOST}")
print("="*60)
# 检查部署包是否存在
if not os.path.exists(LOCAL_PACKAGE):
print(f"\n[ERROR] Cannot find: {LOCAL_PACKAGE}")
print("Please create package first")
return False
file_size = os.path.getsize(LOCAL_PACKAGE) / 1024 / 1024
print(f"\n[OK] Found package: {LOCAL_PACKAGE} ({file_size:.2f} MB)")
# 步骤1: 上传文件
print_step(1, "上传部署包到服务器")
print(f"正在上传 {LOCAL_PACKAGE} 到 {SERVER_HOST}:/root/")
print(f"密码: {SERVER_PASSWORD}")
print("\n请在提示时输入密码...")
time.sleep(2)
upload_cmd = f"scp {LOCAL_PACKAGE} {SERVER_USER}@{SERVER_HOST}:/root/"
result = subprocess.run(upload_cmd, shell=True)
if result.returncode != 0:
print("\n[ERROR] Upload failed!")
return False
print("\n[OK] Upload success!")
# 步骤2: 创建项目目录
print_step(2, "创建项目目录")
commands = [
f"mkdir -p {SERVER_PROJECT_DIR}",
f"cd {SERVER_PROJECT_DIR} && pwd"
]
for cmd in commands:
print(f"执行: {cmd}")
result = subprocess.run(
f'ssh {SERVER_USER}@{SERVER_HOST} "{"; ".join(commands)}"',
shell=True
)
# 步骤3: 备份现有文件
print_step(3, "备份现有上传文件")
backup_cmd = f"""
cd {SERVER_PROJECT_DIR} && \
if [ -d uploads ]; then \
mkdir -p backups && \
echo '正在备份uploads目录...' && \
cp -r uploads backups/uploads-$(date +%Y%m%d-%H%M%S) 2>/dev/null || true && \
echo '[OK] Backup complete'; \
else \
echo '[WARN] No uploads directory found, skip backup'; \
fi
"""
subprocess.run(f'ssh {SERVER_USER}@{SERVER_HOST} "{backup_cmd}"', shell=True)
# 步骤4: 解压文件
print_step(4, "解压新版本")
extract_cmd = f"""
cd {SERVER_PROJECT_DIR} && \
echo '正在解压项目文件...' && \
tar -xzf /root/{LOCAL_PACKAGE} && \
rm /root/{LOCAL_PACKAGE} && \
echo '[OK] Extract complete' && \
ls -lh
"""
subprocess.run(f'ssh {SERVER_USER}@{SERVER_HOST} "{extract_cmd}"', shell=True)
# 步骤5: 恢复上传目录
print_step(5, "恢复上传目录")
restore_cmd = f"""
cd {SERVER_PROJECT_DIR} && \
if [ -d backups ]; then \
latest=$(ls -td backups/uploads-* 2>/dev/null | head -1); \
if [ -n "$latest" ]; then \
echo "从 $latest 恢复上传文件..."; \
mkdir -p uploads; \
cp -r "$latest"/* uploads/ 2>/dev/null || true; \
echo '[OK] Restore complete'; \
fi; \
fi && \
mkdir -p uploads && \
chmod -R 777 uploads && \
echo '[OK] Permissions set'
"""
subprocess.run(f'ssh {SERVER_USER}@{SERVER_HOST} "{restore_cmd}"', shell=True)
# 步骤6: 查看当前Docker状态
print_step(6, "检查Docker状态")
subprocess.run(
f'ssh {SERVER_USER}@{SERVER_HOST} "docker ps -a"',
shell=True
)
# 步骤7: 停止旧容器
print_step(7, "停止旧容器")
stop_cmd = f"""
cd {SERVER_PROJECT_DIR} && \
echo '正在停止旧容器...' && \
docker compose down 2>/dev/null || docker-compose down 2>/dev/null || true && \
echo '[OK] Old containers stopped'
"""
subprocess.run(f'ssh {SERVER_USER}@{SERVER_HOST} "{stop_cmd}"', shell=True)
# 步骤8: 构建并启动新容器
print_step(8, "构建并启动新容器")
print("[WARN] This step may take several minutes, please wait...")
build_cmd = f"""
cd {SERVER_PROJECT_DIR} && \
echo '正在构建镜像...' && \
docker compose up -d --build --force-recreate || docker-compose up -d --build --force-recreate && \
echo '[OK] Containers started'
"""
subprocess.run(f'ssh {SERVER_USER}@{SERVER_HOST} "{build_cmd}"', shell=True)
# 步骤9: 等待容器启动
print_step(9, "等待容器完全启动")
print("等待20秒...")
for i in range(20, 0, -1):
print(f"\r剩余 {i} 秒...", end="", flush=True)
time.sleep(1)
print("\n")
# 步骤10: 数据库迁移
print_step(10, "执行数据库迁移")
migrate_cmd = f"""
cd {SERVER_PROJECT_DIR} && \
echo '正在执行数据库迁移...' && \
docker compose exec -T app npx prisma db push --accept-data-loss --schema=/app/prisma/schema.prisma 2>/dev/null || \
docker-compose exec -T app npx prisma db push --accept-data-loss --schema=/app/prisma/schema.prisma 2>/dev/null || \
echo '[WARN] Database migration failed, may need manual execution'
"""
subprocess.run(f'ssh {SERVER_USER}@{SERVER_HOST} "{migrate_cmd}"', shell=True)
# 步骤11: 查看最终状态
print_step(11, "查看部署结果")
print("\n容器运行状态:")
subprocess.run(f'ssh {SERVER_USER}@{SERVER_HOST} "docker ps"', shell=True)
print("\n\n应用日志(最后30行):")
subprocess.run(
f'ssh {SERVER_USER}@{SERVER_HOST} "cd {SERVER_PROJECT_DIR} && (docker compose logs --tail=30 app 2>/dev/null || docker-compose logs --tail=30 app)"',
shell=True
)
# 完成
print("\n" + "="*60)
print(" [OK] Deployment Complete!")
print("="*60)
print(f"\n访问地址: http://{SERVER_HOST}:端口号")
print("(请根据docker-compose.yml中的端口配置访问)")
print("\n常用命令:")
print(f" 查看日志: ssh {SERVER_USER}@{SERVER_HOST} 'cd {SERVER_PROJECT_DIR} && docker compose logs -f app'")
print(f" 重启服务: ssh {SERVER_USER}@{SERVER_HOST} 'cd {SERVER_PROJECT_DIR} && docker compose restart'")
print(f" 停止服务: ssh {SERVER_USER}@{SERVER_HOST} 'cd {SERVER_PROJECT_DIR} && docker compose down'")
print("="*60)
return True
if __name__ == "__main__":
try:
success = main()
sys.exit(0 if success else 1)
except KeyboardInterrupt:
print("\n\n[INFO] User interrupted")
sys.exit(1)
except Exception as e:
print(f"\n\n[ERROR] Error occurred: {e}")
sys.exit(1)