#!/usr/bin/env python3 """ 修复部署 - 上传配置文件并重启服务 """ import requests import time import hashlib import json # 禁用 SSL 警告 import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # 宝塔配置 BT_PANEL = "http://47.107.172.23:8888" BT_API_KEY = "PKdfnaInQL0P5ghB8SvwbrGcIpXWaEvq" # 部署配置 DEPLOY_PATH = "/www/wwwroot/ai-interview" DOMAIN = "interview.test.ai.ireborn.com.cn" def bt_api(action, data=None, timeout=60): """调用宝塔 API""" if data is None: data = {} request_time = int(time.time()) request_token = hashlib.md5( f"{request_time}{hashlib.md5(BT_API_KEY.encode()).hexdigest()}".encode() ).hexdigest() data['request_time'] = request_time data['request_token'] = request_token url = f"{BT_PANEL}/{action}" try: response = requests.post(url, data=data, timeout=timeout, verify=False) return response.json() except requests.exceptions.JSONDecodeError: return {"status": True, "msg": response.text[:500]} except Exception as e: return {"status": False, "msg": str(e)} def write_remote_file(path, content): """写入远程文件""" print(f"📝 写入文件: {path}") result = bt_api("files?action=SaveFileBody", { "path": path, "data": content, "encoding": "utf-8" }) if result.get("status"): print(" ✅ 文件写入成功") return True print(f" 结果: {result}") return result.get("status", False) def exec_shell(command, timeout=300): """执行 Shell 命令 - 使用宝塔终端 API""" print(f"🔧 执行: {command[:80]}...") # 尝试多个可能的 API 端点 endpoints = [ ("deployment?action=ExecCommand", {"command": command}), ("plugin?action=a&s=deployment&name=exec_command", {"command": command}), ("crontab?action=GetDataList", {}), # 先获取计划任务列表 ] # 方法1: 使用 files 接口执行命令(某些宝塔版本支持) result = bt_api("files?action=ExecCommand", {"command": command}, timeout=timeout) if result.get("status") or "404" not in str(result.get("msg", "")): print(f" 结果: {str(result)[:200]}") return result # 方法2: 创建临时脚本并执行 script_path = "/tmp/bt_exec_cmd.sh" write_result = bt_api("files?action=SaveFileBody", { "path": script_path, "data": f"#!/bin/bash\n{command}\n", "encoding": "utf-8" }) if write_result.get("status"): # 设置执行权限并运行 result = bt_api("files?action=ExecCommand", {"command": f"chmod +x {script_path} && {script_path}"}, timeout=timeout) print(f" 结果: {str(result)[:200]}") return result print(f" ⚠️ 无法执行命令,请手动在服务器执行") return {"status": False, "msg": "需要手动执行"} def restart_nginx(): """重启 Nginx""" print("🔄 重启 Nginx...") result = bt_api("system?action=ServiceAdmin", { "name": "nginx", "type": "restart" }) print(f" 结果: {result}") return result def main(): print("=" * 60) print("🔧 AI 面试系统 - 修复部署") print("=" * 60) print() # 1. 写入 .env 文件 print("\n📝 步骤 1: 写入环境变量文件") env_content = """# AI Interview 后端环境变量 COZE_PAT_TOKEN=pat_nd1wU47WyPS9GCIyJ1clnH8h1WOQXGrYELX8w73TnSZaYbFdYD4swIhzcETBUbfT COZE_BOT_ID=7595113005181386792 COZE_WORKFLOW_A_ID=7597357422713798710 COZE_WORKFLOW_C_ID=7597376294612107318 FILE_SERVER_URL=https://files.test.ai.ireborn.com.cn FILE_SERVER_TOKEN=ai_interview_2026_secret CORS_ORIGINS=http://interview.test.ai.ireborn.com.cn,https://interview.test.ai.ireborn.com.cn,http://localhost:5173 """ write_remote_file(f"{DEPLOY_PATH}/deploy/.env", env_content) # 2. 写入 Nginx 配置 print("\n📝 步骤 2: 写入 Nginx 反向代理配置") nginx_config = """server { listen 80; server_name interview.test.ai.ireborn.com.cn; index index.html; access_log /www/wwwlogs/interview.test.ai.ireborn.com.cn.log; error_log /www/wwwlogs/interview.test.ai.ireborn.com.cn.error.log; # 前端 - 代理到 Docker 前端容器 (3000 端口) location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # API - 代理到 Docker 后端容器 (8000 端口) location /api/ { proxy_pass http://127.0.0.1:8000/api/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 120s; proxy_connect_timeout 120s; } # 健康检查端点 location /health { proxy_pass http://127.0.0.1:8000/health; proxy_http_version 1.1; proxy_set_header Host $host; } } """ write_remote_file(f"/www/server/panel/vhost/nginx/{DOMAIN}.conf", nginx_config) # 3. 写入 Docker 重启脚本 print("\n📝 步骤 3: 写入 Docker 重启脚本") restart_script = f"""#!/bin/bash cd {DEPLOY_PATH}/deploy docker-compose down 2>/dev/null || true sleep 2 docker-compose up -d --build sleep 5 docker ps """ write_remote_file(f"{DEPLOY_PATH}/restart_docker.sh", restart_script) # 4. 重启 Nginx print("\n🔄 步骤 4: 重启 Nginx") restart_nginx() print() print("=" * 60) print("✅ 配置文件已上传!") print("=" * 60) print() print("⚠️ 请在服务器上手动执行以下命令重启 Docker:") print() print(f" chmod +x {DEPLOY_PATH}/restart_docker.sh") print(f" {DEPLOY_PATH}/restart_docker.sh") print() print(" 或者:") print() print(f" cd {DEPLOY_PATH}/deploy") print(" docker-compose down && docker-compose up -d --build") print() print(f"🌐 配置完成后访问:") print(f" 用户端: http://{DOMAIN}") print(f" 管理后台: http://{DOMAIN}/admin") print(f" API: http://{DOMAIN}/api/health") print() if __name__ == "__main__": main()