189 lines
5.3 KiB
Python
Executable File
189 lines
5.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Neural Sync 启动器 - 跨平台版本
|
|
一键启动前后端服务
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
import subprocess
|
|
import signal
|
|
import platform
|
|
from pathlib import Path
|
|
|
|
# 颜色输出
|
|
class Colors:
|
|
GREEN = '\033[0;32m'
|
|
YELLOW = '\033[1;33m'
|
|
RED = '\033[0;31m'
|
|
BLUE = '\033[0;34m'
|
|
NC = '\033[0m'
|
|
|
|
def print_colored(color, text):
|
|
print(f"{color}{text}{Colors.NC}")
|
|
|
|
def get_project_dir():
|
|
return Path(__file__).parent
|
|
|
|
def check_venv():
|
|
venv_dir = get_project_dir() / "venv"
|
|
if not venv_dir.exists():
|
|
print_colored(Colors.RED, "❌ 虚拟环境不存在,请先创建: python -m venv venv")
|
|
sys.exit(1)
|
|
|
|
def kill_port(port):
|
|
"""终止占用指定端口的进程"""
|
|
system = platform.system()
|
|
try:
|
|
if system == "Darwin": # macOS
|
|
subprocess.run(["lsof", "-ti", f"{port}"],
|
|
capture_output=True, text=True, check=True)
|
|
result = subprocess.run(["lsof", "-ti", f"{port}"],
|
|
capture_output=True, text=True)
|
|
if result.stdout.strip():
|
|
subprocess.run(["kill", "-9"] + result.stdout.strip().split(),
|
|
capture_output=True)
|
|
elif system == "Linux":
|
|
result = subprocess.run(["fuser", "-k", f"{port}/tcp"],
|
|
capture_output=True)
|
|
except:
|
|
pass
|
|
|
|
def start_backend():
|
|
"""启动后端服务"""
|
|
print_colored(Colors.GREEN, "▶️ 启动后端服务...")
|
|
|
|
project_dir = get_project_dir()
|
|
backend_dir = project_dir / "backend"
|
|
log_dir = project_dir / "logs"
|
|
log_dir.mkdir(exist_ok=True)
|
|
|
|
# 激活虚拟环境并启动
|
|
venv_python = get_project_dir() / "venv" / "bin" / "python"
|
|
if platform.system() == "Windows":
|
|
venv_python = get_project_dir() / "venv" / "Scripts" / "python.exe"
|
|
|
|
log_file = log_dir / "backend.log"
|
|
|
|
process = subprocess.Popen(
|
|
[str(venv_python), "-m", "uvicorn", "app.main:app", "--reload", "--port", "8000"],
|
|
cwd=backend_dir,
|
|
stdout=open(log_file, "w"),
|
|
stderr=subprocess.STDOUT
|
|
)
|
|
|
|
# 等待后端启动
|
|
for i in range(10):
|
|
try:
|
|
response = subprocess.run(
|
|
["curl", "-s", "http://127.0.0.1:8000/docs"],
|
|
capture_output=True, timeout=2
|
|
)
|
|
if response.returncode == 0:
|
|
print_colored(Colors.GREEN, f"✅ 后端启动成功 (PID: {process.pid})")
|
|
return process
|
|
except:
|
|
pass
|
|
time.sleep(1)
|
|
|
|
print_colored(Colors.RED, "❌ 后端启动失败")
|
|
return None
|
|
|
|
def start_frontend():
|
|
"""启动前端服务"""
|
|
print_colored(Colors.GREEN, "▶️ 启动前端服务...")
|
|
|
|
project_dir = get_project_dir()
|
|
frontend_dir = project_dir / "frontend"
|
|
log_dir = project_dir / "logs"
|
|
log_dir.mkdir(exist_ok=True)
|
|
|
|
log_file = log_dir / "frontend.log"
|
|
|
|
process = subprocess.Popen(
|
|
["npm", "run", "dev", "--", "--host"],
|
|
cwd=frontend_dir,
|
|
stdout=open(log_file, "w"),
|
|
stderr=subprocess.STDOUT,
|
|
shell=True
|
|
)
|
|
|
|
# 等待前端启动
|
|
for i in range(15):
|
|
try:
|
|
response = subprocess.run(
|
|
["curl", "-s", "http://127.0.0.1:5173"],
|
|
capture_output=True, timeout=2
|
|
)
|
|
if response.returncode == 0:
|
|
print_colored(Colors.GREEN, f"✅ 前端启动成功 (PID: {process.pid})")
|
|
return process
|
|
except:
|
|
pass
|
|
time.sleep(1)
|
|
|
|
print_colored(Colors.YELLOW, "⚠️ 前端可能还在启动中")
|
|
return process
|
|
|
|
def main():
|
|
print_colored(Colors.BLUE, "🚀 Neural Sync 启动器")
|
|
print("=" * 30)
|
|
|
|
project_dir = get_project_dir()
|
|
os.chdir(project_dir)
|
|
|
|
# 检查环境
|
|
check_venv()
|
|
|
|
# 清理端口
|
|
print_colored(Colors.BLUE, "🧹 清理端口...")
|
|
kill_port(8000)
|
|
kill_port(5173)
|
|
time.sleep(1)
|
|
|
|
# 启动服务
|
|
backend_proc = start_backend()
|
|
if not backend_proc:
|
|
sys.exit(1)
|
|
|
|
frontend_proc = start_frontend()
|
|
|
|
# 保存 PID
|
|
log_dir = project_dir / "logs"
|
|
(log_dir / "backend.pid").write_text(str(backend_proc.pid))
|
|
(log_dir / "frontend.pid").write_text(str(frontend_proc.pid))
|
|
|
|
# 显示结果
|
|
print()
|
|
print_colored(Colors.GREEN, "🎉 所有服务启动完成!")
|
|
print("=" * 30)
|
|
print(f" 后端: {Colors.YELLOW}http://127.0.0.1:8000{Colors.NC}")
|
|
print(f" 前端: {Colors.YELLOW}http://127.0.0.1:5173{Colors.NC}")
|
|
print(f" API 文档: {Colors.YELLOW}http://127.0.0.1:8000/docs{Colors.NC}")
|
|
print()
|
|
print("📝 日志文件:")
|
|
print(" 后端: logs/backend.log")
|
|
print(" 前端: logs/frontend.log")
|
|
print()
|
|
print_colored(Colors.YELLOW, "💡 按 Ctrl+C 停止所有服务,或运行: python stop.py")
|
|
|
|
# 等待中断信号
|
|
def signal_handler(sig, frame):
|
|
print_colored(Colors.YELLOW, "\n🛑 正在停止服务...")
|
|
backend_proc.terminate()
|
|
frontend_proc.terminate()
|
|
print_colored(Colors.GREEN, "🎉 所有服务已停止")
|
|
sys.exit(0)
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
# 保持运行
|
|
try:
|
|
backend_proc.wait()
|
|
except KeyboardInterrupt:
|
|
signal_handler(None, None)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|