# Goal Planner Linux 部署指南 ## 环境要求 ### 服务器 - Linux 服务器(Ubuntu 20.04+ / CentOS 7+) - Python 3.11+(由 uv 管理,不依赖系统 Python) - MySQL 5.7+ / 8.0(需提前安装) - Nginx(需提前安装) - [uv](https://docs.astral.sh/uv/)(Python 包管理工具) > **关于 Python 版本**:项目要求 Python ≥ 3.10。CentOS 7 系统自带 Python 2.7.5,无需升级或卸载,uv 会自动下载和管理独立的 Python 版本,不会与系统 Python 冲突。系统工具(如 yum)依赖的 Python 2.7.5 完全不受影响。**推荐使用 Python 3.11**,wheel 生态最成熟,所有依赖都有预编译 wheel,安装秒完成;Python 3.12/3.14 部分包(如 greenlet、pydantic-core)在 CentOS 7 上缺少兼容的预编译 wheel,需要从源码编译,而 CentOS 7 的 GCC 4.8.5 不支持 C++11,会导致编译失败。 ### 本地(仅构建前端时需要) - Node.js 18+ --- ## 1. 安装 uv ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` 安装完成后,需要让 uv 命令生效(二选一): ```bash # 方式一:执行以下命令(推荐,立即生效) source $HOME/.local/bin/env # 方式二:重新连接 SSH ``` 验证安装: ```bash uv --version ``` --- ## 2. 配置 MySQL ```bash # 登录 MySQL sudo mysql -u root -p # 创建数据库和用户 CREATE DATABASE goal_planner CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'goal_planner'@'localhost' IDENTIFIED BY '你的数据库密码'; GRANT ALL PRIVILEGES ON goal_planner.* TO 'goal_planner'@'localhost'; FLUSH PRIVILEGES; EXIT; ``` > 首次启动后端时,SQLAlchemy 会自动创建表结构,无需手动导入。 --- ## 3. 上传项目 将项目上传到服务器,部署目录为 `/home/wwwroot/goal-planner`: ```bash sudo mkdir -p /home/wwwroot/goal-planner # 方式一:git clone cd /home/wwwroot/goal-planner git clone <你的仓库地址> . # 方式二:scp 上传 # scp -r ./goal-planner/* user@server:/home/wwwroot/goal-planner/ ``` --- ## 4. 后端部署 ### 4.1 配置环境变量 ```bash cd /home/wwwroot/goal-planner/backend cp .env.example .env ``` 编辑 `.env` 文件: ```bash vi .env ``` 填入实际配置: ```env MYSQL_HOST=localhost MYSQL_PORT=3306 MYSQL_USER=goal_planner MYSQL_PASSWORD=你的数据库密码 MYSQL_DATABASE=goal_planner SECRET_KEY=替换为一个随机长字符串 ACCESS_TOKEN_EXPIRE_MINUTES=1440 LLM_PROVIDER=dashscope OPENAI_API_KEY=你的API密钥 OPENAI_API_URL=https://dashscope.aliyuncs.com/compatible-mode/v1 OPENAI_MODEL=qwen3-plus ENABLE_SEARCH=true ``` > **重要**:`SECRET_KEY` 必须替换为随机字符串,可用 `openssl rand -hex 32` 生成。 ### 4.2 使用 uv 创建虚拟环境并安装依赖 ```bash cd /home/wwwroot/goal-planner # 创建虚拟环境,uv 会自动下载 Python 3.11(不依赖系统 Python) uv venv .venv --python 3.11 # 激活虚拟环境 source .venv/bin/activate # 安装依赖(使用清华镜像加速,强制只安装预编译 wheel) uv pip install -r backend/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple --only-binary :all: ``` > **说明**:`uv venv --python 3.11` 会将 Python 3.11 下载到 `~/.local/share/uv/python/` 目录,仅 uv 使用,不会覆盖或修改系统自带的 Python 2.7.5。虚拟环境放在项目根目录 `/home/wwwroot/goal-planner/.venv`,与 backend 同级。`--only-binary :all:` 强制只安装预编译 wheel,避免在 CentOS 7 的旧 GCC 上从源码编译失败。 ### 4.3 验证后端启动 ```bash cd /home/wwwroot/goal-planner/backend source /home/wwwroot/goal-planner/.venv/bin/activate uvicorn main:app --host 127.0.0.1 --port 8000 ``` 看到 `Uvicorn running on http://127.0.0.1:8000` 即启动成功,`Ctrl+C` 停止。 ### 4.4 后台运行 验证无误后,使用以下方式后台运行(推荐方式二): **方式一:nohup(简单快速)** ```bash cd /home/wwwroot/goal-planner/backend source /home/wwwroot/goal-planner/.venv/bin/activate nohup uvicorn main:app --host 127.0.0.1 --port 8000 > /home/wwwroot/goal-planner/backend/app.log 2>&1 & ``` 常用操作: ```bash # 查看日志 tail -f /home/wwwroot/goal-planner/backend/app.log # 查看进程 ps aux | grep uvicorn # 停止服务 kill $(ps aux | grep 'uvicorn main:app' | grep -v grep | awk '{print $2}') ``` **方式二:systemd 服务(推荐,开机自启 + 自动重启)** 见第 7 章。 --- ## 5. 前端构建与部署 ### 5.1 修改 API 接口地址 前端 API 地址硬编码在 `frontend/services/apiService.ts` 中: ```typescript const API_BASE_URL = 'http://localhost:8000'; ``` **部署前必须修改为实际的后端地址**。根据你的部署方式选择: #### 方式一:使用 Nginx 反向代理(推荐) 将 API 地址改为相对路径,由 Nginx 统一代理: ```typescript const API_BASE_URL = '/api-proxy'; ``` 这样前端请求 `/api-proxy/api/generate/` 会被 Nginx 转发到后端 `http://127.0.0.1:8000/api/generate/`,无需暴露后端端口。 #### 方式二:直接指向后端地址 如果你的后端运行在独立域名或端口上: ```typescript const API_BASE_URL = 'http://你的服务器IP:8000'; ``` > **注意**:此方式需要后端开放 8000 端口,并在 `main.py` 的 CORS 配置中添加前端域名。 ### 5.2 修改后端 CORS 配置(方式一不需要) 如果使用方式二,需要修改 `backend/main.py` 中的 CORS 允许来源: ```python origins = [ "http://你的域名", "http://你的服务器IP", ] ``` ### 5.3 本地构建前端 在本地开发机器上执行构建(不需要在服务器上安装 Node.js): ```bash cd frontend # 安装依赖 npm install # 构建 npm run build ``` 构建产物在 `frontend/dist/` 目录中。 ### 5.4 上传静态文件到服务器 将本地构建产物上传到服务器: ```bash # 在本地执行 scp -r frontend/dist/* user@你的服务器IP:/var/www/goal-planner/ ``` 或在服务器上从项目目录复制(如果已上传整个项目): ```bash sudo mkdir -p /var/www/goal-planner sudo cp -r /home/wwwroot/goal-planner/frontend/dist/* /var/www/goal-planner/ ``` --- ## 6. 配置 Nginx 创建 Nginx 配置文件: ```bash sudo vi /etc/nginx/conf.d/goal-planner.conf ``` ### 方式一:Nginx 反向代理(推荐) 前后端统一通过 Nginx 访问,后端不对外暴露端口: ```nginx server { listen 80; server_name 你的域名或IP; # 前端静态文件 root /var/www/goal-planner; index index.html; # 前端路由 - SPA 应用所有路径都返回 index.html location / { try_files $uri $uri/ /index.html; } # 后端 API 反向代理 location /api-proxy/ { rewrite ^/api-proxy/(.*) /$1 break; proxy_pass http://127.0.0.1:8000; 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; # SSE 支持 - 关键配置 proxy_buffering off; proxy_cache off; proxy_read_timeout 300s; proxy_http_version 1.1; proxy_set_header Connection ''; } # 静态资源缓存 location /assets/ { expires 30d; add_header Cache-Control "public, immutable"; } # 禁止访问隐藏文件 location ~ /\. { deny all; } } ``` > **SSE 关键配置说明**: > - `proxy_buffering off` — 禁用缓冲,确保 SSE 事件实时推送到客户端 > - `proxy_cache off` — 禁用缓存 > - `proxy_read_timeout 300s` — AI 生成可能耗时较长,增大超时时间 > - `proxy_http_version 1.1` + `Connection ''` — 保持长连接 ### 方式二:前后端分离部署 后端直接监听 8000 端口对外服务: ```nginx server { listen 80; server_name 你的域名或IP; root /var/www/goal-planner; index index.html; location / { try_files $uri $uri/ /index.html; } location /assets/ { expires 30d; add_header Cache-Control "public, immutable"; } } ``` 此方式下 `apiService.ts` 中的 `API_BASE_URL` 应设为 `http://你的域名或IP:8000`。 ### 验证并重启 Nginx ```bash sudo nginx -t sudo systemctl restart nginx ``` --- ## 7. 使用 systemd 管理后端进程 创建 systemd 服务文件,实现开机自启和进程管理: ```bash sudo vi /etc/systemd/system/goal-planner.service ``` ```ini [Unit] Description=Goal Planner Backend API After=network.target mysql.service [Service] Type=simple User=root WorkingDirectory=/home/wwwroot/goal-planner/backend ExecStart=/home/wwwroot/goal-planner/.venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 Restart=always RestartSec=5 Environment=PYTHONUNBUFFERED=1 [Install] WantedBy=multi-user.target ``` > 如果使用方式二(前后端分离),将 `--host 127.0.0.1` 改为 `--host 0.0.0.0` 以允许外部访问。 启动并设置开机自启: ```bash sudo systemctl daemon-reload sudo systemctl start goal-planner sudo systemctl enable goal-planner ``` 常用管理命令: ```bash sudo systemctl status goal-planner # 查看状态 sudo systemctl restart goal-planner # 重启 sudo systemctl stop goal-planner # 停止 sudo journalctl -u goal-planner -f # 查看实时日志 ``` --- ## 8. 验证部署 ```bash # 检查后端健康状态 curl http://127.0.0.1:8000/api/health # 预期返回: {"status":"ok"} # 检查 Nginx 代理(方式一) curl http://你的域名或IP/api-proxy/api/health # 预期返回: {"status":"ok"} # 浏览器访问 # http://你的域名或IP ``` --- ## 9. HTTPS 配置(可选但推荐) 使用 Let's Encrypt 免费证书: ```bash sudo apt install certbot python3-certbot-nginx # Ubuntu sudo yum install certbot python3-certbot-nginx # CentOS sudo certbot --nginx -d 你的域名 ``` Certbot 会自动修改 Nginx 配置并配置 SSL 证书自动续期。 --- ## 10. 更新部署 当代码更新后,执行以下步骤: ### 后端更新 ```bash # 服务器上拉取最新代码 cd /home/wwwroot/goal-planner git pull # 更新后端依赖(如有变动) source /home/wwwroot/goal-planner/.venv/bin/activate uv pip install -r backend/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple --only-binary :all: # 重启后端 sudo systemctl restart goal-planner ``` ### 前端更新 ```bash # 本地构建 cd frontend npm install npm run build # 上传到服务器 scp -r dist/* user@你的服务器IP:/var/www/goal-planner/ ``` --- ## 文件结构总览 ``` /home/wwwroot/goal-planner/ ├── .venv/ # uv 创建的 Python 虚拟环境(项目根目录) ├── backend/ │ ├── .env # 环境变量配置(含数据库密码、API密钥) │ ├── .env.example # 环境变量模板 │ ├── main.py # FastAPI 入口 │ ├── config.py # 配置读取 │ ├── database.py # 数据库连接 │ ├── requirements.txt # Python 依赖 │ ├── models/ # 数据模型 │ └── routes/ # API 路由 │ ├── auth.py │ ├── generate.py │ └── plans.py └── frontend/ ├── dist/ # 构建产物 → 部署到 /var/www/goal-planner/ ├── services/ │ └── apiService.ts # API 地址配置(需修改 API_BASE_URL) ├── package.json └── vite.config.ts /var/www/goal-planner/ # Nginx 静态文件目录 /etc/nginx/conf.d/goal-planner.conf # Nginx 配置 /etc/systemd/system/goal-planner.service # 后端服务配置 ```