212 lines
6.7 KiB
Python
212 lines
6.7 KiB
Python
#!/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()
|