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

246
deploy/bt_deploy.py Normal file
View File

@@ -0,0 +1,246 @@
#!/usr/bin/env python3
"""
通过宝塔 API 自动部署 AI 面试系统
"""
import requests
import time
import hashlib
import os
import base64
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, files=None):
"""调用宝塔 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:
if files:
response = requests.post(url, data=data, files=files, timeout=300, verify=False)
else:
response = requests.post(url, data=data, timeout=60, verify=False)
return response.json()
except requests.exceptions.JSONDecodeError:
return {"status": True, "msg": response.text}
except Exception as e:
return {"status": False, "msg": str(e)}
def create_directory(path):
"""创建目录"""
print(f"📁 创建目录: {path}")
result = bt_api("files?action=CreateDir", {"path": path})
if result.get("status") or "已存在" in str(result.get("msg", "")):
print(" ✅ 目录已就绪")
return True
print(f" ⚠️ {result}")
return True # 继续执行
def upload_file(local_path, remote_dir):
"""上传文件到服务器"""
filename = os.path.basename(local_path)
print(f"📤 上传文件: {filename}")
with open(local_path, 'rb') as f:
files = {'f_path': (filename, f)}
data = {'f_path': remote_dir, 'f_name': filename}
result = bt_api("files?action=upload", data, files)
if result.get("status") or result.get("msg") == "上传成功":
print(f" ✅ 上传成功")
return True
print(f" ❌ 上传失败: {result}")
return False
def exec_shell(command):
"""执行 Shell 命令"""
print(f"🔧 执行命令: {command[:80]}...")
result = bt_api("files?action=ExecShell", {"command": command})
return result
def create_website(domain):
"""创建网站"""
print(f"🌐 创建网站: {domain}")
data = {
"webname": json.dumps({"domain": domain, "domainlist": [], "count": 0}),
"path": f"/www/wwwroot/{domain}",
"type_id": 0,
"type": "PHP",
"version": "00",
"port": "80",
"ps": "AI面试系统",
"ftp": "false",
"sql": "false"
}
result = bt_api("site?action=AddSite", data)
if result.get("status") or result.get("siteStatus"):
print(" ✅ 网站创建成功")
return True
if "已存在" in str(result.get("msg", "")):
print(" ⚠️ 网站已存在")
return True
print(f" 结果: {result}")
return True
def set_proxy(domain, target_url):
"""设置反向代理"""
print(f"🔄 设置反向代理: {domain} -> {target_url}")
# 获取站点 ID
sites_result = bt_api("site?action=GetSitesSort")
site_id = None
if sites_result.get("data"):
for site in sites_result["data"]:
if site.get("name") == domain:
site_id = site.get("id")
break
if not site_id:
print(" ⚠️ 未找到站点 ID尝试继续...")
return True
# 设置代理
proxy_data = {
"sitename": domain,
"proxyname": "ai-interview",
"proxydir": "/",
"proxysite": target_url,
"proxysend": "0",
"cache": "0",
"cacheTime": "1",
"subfilter": "[]",
"type": "1",
"advanced": "0"
}
result = bt_api("site?action=CreateProxy", proxy_data)
print(f" 结果: {result}")
return True
def write_remote_file(path, content):
"""写入远程文件"""
print(f"📝 写入文件: {path}")
# 使用 base64 编码内容
encoded = base64.b64encode(content.encode()).decode()
result = bt_api("files?action=SaveFileBody", {
"path": path,
"data": content,
"encoding": "utf-8"
})
if result.get("status"):
print(" ✅ 文件写入成功")
return True
print(f" 结果: {result}")
return True
def main():
print("=" * 60)
print("🚀 AI 语音面试系统 - 自动部署")
print("=" * 60)
print()
# 1. 创建部署目录
print("\n📦 步骤 1: 创建目录结构")
create_directory(DEPLOY_PATH)
create_directory(f"{DEPLOY_PATH}/frontend")
create_directory(f"{DEPLOY_PATH}/backend")
create_directory(f"{DEPLOY_PATH}/deploy")
create_directory(f"{DEPLOY_PATH}/deploy/nginx")
# 2. 写入配置文件
print("\n📝 步骤 2: 写入配置文件")
# .env 文件
env_content = """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
"""
write_remote_file(f"{DEPLOY_PATH}/deploy/.env", env_content)
# 3. 上传部署包
print("\n📤 步骤 3: 上传部署包")
tar_path = "/Users/a111/Documents/AgentWD/projects/ai-interview-deploy.tar.gz"
if os.path.exists(tar_path):
upload_file(tar_path, "/www/wwwroot")
else:
print(f" ⚠️ 部署包不存在: {tar_path}")
# 4. 解压并部署
print("\n🔧 步骤 4: 解压部署包")
commands = [
f"cd /www/wwwroot && tar -xzf ai-interview-deploy.tar.gz -C {DEPLOY_PATH}",
f"cd {DEPLOY_PATH}/deploy && cp .env .env.bak 2>/dev/null || true",
]
for cmd in commands:
exec_shell(cmd)
time.sleep(1)
# 5. 启动 Docker
print("\n🐳 步骤 5: 启动 Docker 容器")
docker_commands = [
f"cd {DEPLOY_PATH}/deploy && docker-compose down 2>/dev/null || true",
f"cd {DEPLOY_PATH}/deploy && docker-compose up -d --build",
]
for cmd in docker_commands:
print(f" 执行: {cmd}")
result = exec_shell(cmd)
print(f" 结果: {result}")
time.sleep(3)
# 6. 创建网站和反向代理
print("\n🌐 步骤 6: 配置网站")
create_website(DOMAIN)
time.sleep(2)
set_proxy(DOMAIN, "http://127.0.0.1:3000")
# 7. 检查状态
print("\n🔍 步骤 7: 检查部署状态")
result = exec_shell("docker ps --format 'table {{.Names}}\\t{{.Status}}\\t{{.Ports}}'")
print(f" 容器状态: {result}")
print()
print("=" * 60)
print("✅ 部署完成!")
print("=" * 60)
print()
print(f"🌐 访问地址:")
print(f" 用户端: http://{DOMAIN}")
print(f" 管理后台: http://{DOMAIN}/admin")
print()
print("⚠️ 如需 HTTPS请在宝塔面板中为该网站申请 SSL 证书")
print("=" * 60)
if __name__ == "__main__":
main()