Initial commit: AI Interview System

This commit is contained in:
111
2026-01-23 13:57:48 +08:00
commit 95770afe21
127 changed files with 24686 additions and 0 deletions

211
deploy/fix_deploy.py Normal file
View File

@@ -0,0 +1,211 @@
#!/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()