diff --git a/DESIGN.md b/DESIGN.md index 5224eab..81fc5c3 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -7,6 +7,14 @@ CareerBot 是一个个人职业展示网站,集成了 AI 智能对话助手。 - **访问者(招聘方)**:浏览候选人的职业背景,通过 AI 对话深入了解候选人,上传 JD 进行匹配分析 - **管理员(候选人)**:管理个人资料、教育/工作经历、技能等内容,配置 LLM,管理访问令牌,查看招聘意向消息,生成定制简历 +### 线上地址 + +| 用途 | 地址 | +|------|------| +| 主页 | `http://www.ityb.me/careerbot/` | +| 管理后台 | `http://www.ityb.me/careerbot/admin/login` | +| Gitea 代码仓库 | `http://www.ityb.me:3000` | + ### 技术栈 | 层级 | 技术 | @@ -19,6 +27,7 @@ CareerBot 是一个个人职业展示网站,集成了 AI 智能对话助手。 | 认证 | JWT (管理员) / Cookie (访问者) | | 密码加密 | bcrypt | | 文件处理 | python-docx, PyPDF2, Pillow | +| 反向代理 | Nginx (子路径部署) | --- @@ -28,8 +37,8 @@ CareerBot 是一个个人职业展示网站,集成了 AI 智能对话助手。 CareerBot/ ├── app/ │ ├── __init__.py -│ ├── main.py # FastAPI 应用入口,启动初始化 -│ ├── config.py # Pydantic Settings 配置 +│ ├── main.py # FastAPI 应用入口,路由挂载,启动初始化 +│ ├── config.py # Pydantic Settings 配置(含 BASE_PATH) │ ├── database.py # SQLAlchemy 引擎、Session、init_db │ ├── models.py # 全部 ORM 模型定义 │ ├── routers/ @@ -125,6 +134,39 @@ CareerBot/ └─────────────────────┘ ``` +### 3.1 子路径部署架构(BASE_PATH) + +CareerBot 部署在域名的 `/careerbot` 子路径下,通过 `BASE_PATH` 配置实现全局路径前缀管理: + +``` +浏览器请求: http://www.ityb.me/careerbot/api/profile + │ + ▼ + Nginx (:80) + location /careerbot/ → proxy_pass http://127.0.0.1:8000/careerbot/ + │ + ▼ + FastAPI (uvicorn :8000) + app.include_router(public.router, prefix="/careerbot") + → 匹配路由: @router.get("/api/profile") + → 实际路径: /careerbot/api/profile +``` + +**前缀注入机制:** + +| 层级 | 方式 | 说明 | +|------|------|------| +| Python 路由 | `app.include_router(prefix=BASE)` | 所有路由自动加前缀,路由装饰器不变 | +| 静态文件 | `app.mount(f"{BASE}/static", ...)` | CSS/JS/图片通过 `/careerbot/static/` 访问 | +| Jinja2 模板 | `templates.env.globals["base"]` | 模板中用 `{{ base }}` 引用前缀 | +| 前端 JS | `window.BASE_PATH` | 在模板中注入,JS 文件通过全局变量使用 | +| RedirectResponse | `url=f"{BASE}/login"` | Python 重定向使用完整路径 | +| DB 存储路径 | `photo_url = f"{BASE}/uploads/..."` | 持久化数据中的 URL 包含前缀 | + +**配置位置:** +- `app/config.py` → `BASE_PATH: str = "/careerbot"` +- 修改此值即可将应用迁移到其他子路径 + --- ## 4. 数据模型(类图) @@ -641,8 +683,11 @@ data: {"content": "", "done": true} ← 结束标记 ### 前端处理逻辑 (chat.js) ```javascript +// 0. BASE_PATH 由模板注入: window.BASE_PATH = "{{ base }}" +const BASE_PATH = window.BASE_PATH || ''; + // 1. POST FormData (session_id + message + file?) -const response = await fetch('/api/chat', { method: 'POST', body: formData }); +const response = await fetch(BASE_PATH + '/api/chat', { method: 'POST', body: formData }); // 2. 读取 ReadableStream const reader = response.body.getReader(); @@ -696,3 +741,5 @@ while (true) { | BackgroundTask 做意图检测 | 避免阻塞 SSE 流或导致连接异常 | | 意图检测用独立 DB Session | BackgroundTask 执行时请求级 Session 已关闭 | | FAB 按钮用内层 span flex | Chrome 对 button 元素的 flex 布局有渲染 bug | +| 子路径部署 + BASE_PATH | 支持同域名多项目部署,一处配置全局生效 | +| Jinja2 globals + window.BASE_PATH | 模板和静态 JS 统一获取路径前缀,避免硬编码 | diff --git a/OPS_MANUAL.md b/OPS_MANUAL.md index 6d76a3c..9c96ca0 100644 --- a/OPS_MANUAL.md +++ b/OPS_MANUAL.md @@ -6,6 +6,7 @@ |------|------| | 云服务商 | 阿里云 ECS | | 公网 IP | `39.106.14.107` | +| 域名 | `ityb.me` / `www.ityb.me` | | 操作系统 | Alibaba Cloud Linux 3 (OpenAnolis Edition) | | 配置 | 2 核 CPU / 2GB 内存 / 40GB 磁盘 | | Python 版本 | 3.11.13 | @@ -36,8 +37,9 @@ ssh root@39.106.14.107 # root 直连 | 系统 | 地址 | 账号 | 密码 | |------|------|------|------| -| CareerBot 管理后台 | `http://39.106.14.107/admin/login` | `ln0422@gmail.com` | `qshs123456` | -| Gitea 代码管理 | `http://39.106.14.107:3000` | `ln0422` | `Qshs123456_` | +| CareerBot 主页 | `http://www.ityb.me/careerbot/` | (访问令牌或匿名) | - | +| CareerBot 管理后台 | `http://www.ityb.me/careerbot/admin/login` | `ln0422@gmail.com` | `qshs123456` | +| Gitea 代码管理 | `http://www.ityb.me:3000` | `ln0422` | `Qshs123456_` | ### 2.3 ECS 用户密码 @@ -52,21 +54,24 @@ ssh root@39.106.14.107 # root 直连 ## 3. 服务架构 ``` -外部访问 +外部访问 (www.ityb.me) │ ▼ -┌─────────────────────────────────────────┐ -│ Nginx (:80) │ -│ 配置: /etc/nginx/sites-enabled/ │ -│ 日志: /var/log/nginx/ │ -├─────────────────────────────────────────┤ -│ http://39.106.14.107/ │ -│ → proxy_pass http://127.0.0.1:8000 │ -│ → CareerBot (uvicorn) │ -├─────────────────────────────────────────┤ -│ http://39.106.14.107:3000 │ -│ → Gitea (直接监听,未经 Nginx 代理) │ -└─────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────┐ +│ Nginx (:80) │ +│ 配置: /etc/nginx/sites-enabled/ │ +│ 日志: /var/log/nginx/ │ +├──────────────────────────────────────────────────────┤ +│ http://www.ityb.me/ │ +│ → 302 重定向到 /careerbot/ │ +│ │ +│ http://www.ityb.me/careerbot/ │ +│ → proxy_pass http://127.0.0.1:8000/careerbot/ │ +│ → CareerBot (uvicorn, FastAPI prefix=/careerbot) │ +├──────────────────────────────────────────────────────┤ +│ http://www.ityb.me:3000 │ +│ → Gitea (直接监听,未经 Nginx 代理) │ +└──────────────────────────────────────────────────────┘ ``` ### 端口使用 @@ -226,12 +231,12 @@ WantedBy=multi-user.target ```nginx server { listen 80; - server_name 39.106.14.107; + server_name www.ityb.me ityb.me 39.106.14.107; client_max_body_size 10M; - location / { - proxy_pass http://127.0.0.1:8000; + location /careerbot/ { + proxy_pass http://127.0.0.1:8000/careerbot/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -242,16 +247,20 @@ server { proxy_read_timeout 300s; } - location /uploads/ { - alias /home/deploy/apps/CareerBot/uploads/; + # 根路径重定向到 CareerBot + location = / { + return 302 /careerbot/; } } ``` 关键配置说明: +- **子路径部署**:CareerBot 部署在 `/careerbot/` 子路径下,后续其他项目可使用其他路径 +- `proxy_pass` 尾部带 `/careerbot/`:将完整路径透传给 FastAPI(FastAPI 通过 `prefix="/careerbot"` 匹配路由) - `proxy_buffering off` + `proxy_cache off`:**必须关闭**,否则 SSE 流式对话无法实时返回 - `proxy_read_timeout 300s`:LLM 长回复可能需要较长时间 - `client_max_body_size 10M`:允许上传最大 10MB 文件 +- 静态文件和上传文件通过 FastAPI StaticFiles 挂载在 `/careerbot/static/` 和 `/careerbot/uploads/`,无需单独 Nginx location ### 6.3 防火墙规则 @@ -330,7 +339,7 @@ uvicorn app.main:app --host 127.0.0.1 --port 8000 ### 8.2 AI 对话不工作 -- 检查管理后台 LLM Config 是否已配置(API URL、API Key、Model Name) +- 检查管理后台(`http://www.ityb.me/careerbot/admin/llm-config`)LLM Config 是否已配置(API URL、API Key、Model Name) - 点击 "Test Connection" 测试连通性 - 确认 ECS 能访问外网:`curl https://api.deepseek.com` @@ -364,24 +373,25 @@ ps aux --sort=-rss | head -10 后续在同一台 ECS 上部署新项目的标准流程: -1. **Gitea 上创建仓库**:`http://39.106.14.107:3000` → New Repository +1. **Gitea 上创建仓库**:`http://www.ityb.me:3000` → New Repository 2. **本地推送代码** 3. **ECS 上克隆并配置**: ```bash cd ~/apps - git clone http://39.106.14.107:3000/ln0422/新项目.git + git clone http://www.ityb.me:3000/ln0422/新项目.git # 安装依赖、初始化等 ``` 4. **创建 systemd 服务**(参考 careerbot.service,注意更换端口号) -5. **添加 Nginx 站点配置**: +5. **添加 Nginx 站点配置**(在同一个 `careerbot.conf` 文件或新建独立 conf 文件中,添加新的 `location /新路径/` 块): ```bash sudo vim /etc/nginx/sites-available/新项目.conf sudo ln -s /etc/nginx/sites-available/新项目.conf /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx ``` + > 建议参考 CareerBot 的子路径部署模式:`location /新路径/ { proxy_pass http://127.0.0.1:新端口/新路径/; }` 6. **如需新端口**:防火墙 + 阿里云安全组同步放行 @@ -393,4 +403,4 @@ ps aux --sort=-rss | head -10 2. **Gitea 端口**:3000 端口对外开放,建议设置强密码,或后续通过 Nginx 代理并限制访问 3. **数据库文件**:`careerbot.db` 不应对外暴露,uvicorn 仅监听 127.0.0.1 已确保安全 4. **定期更新**:定期执行 `sudo dnf update -y` 更新系统安全补丁 -5. **HTTPS**:后续绑定域名后建议配置 Let's Encrypt SSL 证书 +5. **HTTPS**:域名 `ityb.me` 已绑定,建议配置 Let's Encrypt SSL 证书(`certbot --nginx -d www.ityb.me -d ityb.me`)