提交 c6a688e4 authored 作者: 陈泽健's avatar 陈泽健

fix(deploy): 修复容器部署时根路径返回 JSON 而非前端页面

- root() 增加 frontend/dist/ 存在性检查
- 容器部署时返回 index.html,本地开发时返回 JSON 健康检查
- 解决 @app.get("/") 精确匹配抢占 SPA 回退路由的问题
Co-Authored-By: 's avatarClaude <noreply@anthropic.com>
上级 fd2c2f4d
# HANDOFF — 平台自动化测试系统交接文档(V2 部署架构升级) # HANDOFF — 平台自动化测试系统交接文档(V2 部署架构升级 + 三期修复
> **生成时间**: 2026-07-28(最后一次更新:**P1 卡点已解决** ✅ — UA 伪装修复 do_login 成功,端到端用例执行通过 > **生成时间**: 2026-07-29(三期修复:P1 提交入库 ✅ + 根路径 `/` 修复 ✅
> **当前分支**: `platform-auto-test` > **当前分支**: `platform-auto-test`
> **最近提交**: `4cff19b6` feat(deploy): V2部署架构升级 — 合一容器+MySQL(已推送 origin) > **最近提交**: `fd2c2f4d` fix(executor): 修复容器内 HeadlessChrome UA 导致登录失败(已推送 origin)
> **状态**: ✅ V2 部署已上线 + 代码已提交;✅ **P1 卡点已解决**:根因确认为 **HeadlessChrome UA 被被测站点区别对待**(容器内 `input:visible=0`),通过伪装 UA + 隐藏自动化特征修复,`do_login()` 返回 True,2 步用例 11.81s 执行通过。修复已部署到服务器容器,**待 commit 提交**。详见第四节 4.2 > **状态**: ✅ V2 部署已上线运行;✅ P1 卡点已解决并入库;✅ 根路径 `/` 修复已部署;**未提交变更**:root() 根路径修复(`main.py`)
--- ---
...@@ -16,13 +16,11 @@ ...@@ -16,13 +16,11 @@
- Git 提交协作 - Git 提交协作
**当前多窗口状态**:本项目有多个窗口分工开发,交接文档分散在: **当前多窗口状态**:本项目有多个窗口分工开发,交接文档分散在:
- `HANDOFF.md`(本文档)— **V2 部署架构升级**(最新,本次会话) - `HANDOFF.md`(本文档)— **V2 部署架构升级 + 三期修复**(最新,本次会话)
- `HANDOFF_V2部署升级.md` — V2 部署详细交接(与本文档同步,含完整运维命令) - `HANDOFF_V2部署升级.md` — V2 部署详细交接
- `HANDOFF_UI自动化.md` — UI 自动化测试模块 - `HANDOFF_UI自动化.md` — UI 自动化测试模块
- `HANDOFF_安全测试.md` — 安全测试模块 - `HANDOFF_安全测试.md` — 安全测试模块
**下次会话优先读本文档**(V2 部署已完成上线,但有未提交变更需处理)。
--- ---
## 一、项目概览 ## 一、项目概览
...@@ -35,7 +33,7 @@ ...@@ -35,7 +33,7 @@
- **远程仓库**: http://git.ubainsyun.com/bing/ubains-module-test.git - **远程仓库**: http://git.ubainsyun.com/bing/ubains-module-test.git
- **当前分支**: `platform-auto-test`(主分支:`master` - **当前分支**: `platform-auto-test`(主分支:`master`
### V2 部署架构(本次会话上线) ### V2 部署架构
``` ```
服务器 192.168.5.60 (Ubuntu) 服务器 192.168.5.60 (Ubuntu)
...@@ -48,225 +46,99 @@ ...@@ -48,225 +46,99 @@
└── mysql 容器 :3307(外部)/3306(内部) └── mysql 容器 :3307(外部)/3306(内部)
└── mysql/data/ → volume 持久化 └── mysql/data/ → volume 持久化
部署目录: /data/third_party/plat-auto-test/(独立,与 8088 维护平台隔离) 部署目录: /data/third_party/plat-auto-test/
``` ```
--- ---
## 二、本次会话任务(已完成) ## 二、本次会话完成的工作
### V2 部署架构升级 — 合一容器 + MySQL ✅
**任务背景**:将部署从 V1(3 容器:前端 Nginx + 后端 + SQLite)升级到 V2(2 容器:合一 app + MySQL 8.0),解决代码更新需重建镜像、前端无法热更新、SQLite 并发锁、部署目录与 8088 维护平台共用等问题。 ### 1. ✅ P1 修复提交入库(commit `fd2c2f4d`)
**完成的工作** - **内容**: `playwright_executor.py` UA 伪装修复(容器内 HeadlessChrome UA 被被测站点区别对待)
- **文件**: `backend/app/executors/playwright_executor.py` + `HANDOFF.md`(状态同步)
- **已推送**: `git push origin platform-auto-test`
- 至此本地代码与服务器容器状态完全对齐
1. **本地代码改造** ### 2. ✅ 根路径 `/` 修复 — 容器部署时返回前端页面
- 新建合一 Dockerfile(镜像仅装环境,代码 volume 挂载)
- 重写 docker-compose.yml(2 服务:app + mysql)
- 新建 init.sql(MySQL 字符集 + 权限 + 时区)
- 改造 main.py(新增 StaticFiles 挂载前端 + SPA 路由回退)
- 改造 requirements.txt(新增 aiomysql + cryptography)
- 改造 database.py(区分 SQLite/MySQL 连接参数)
- 编写 migrate_sqlite_to_mysql.py(断点续传 + 外键处理)
2. **服务器部署** - **问题**: 访问 `http://192.168.5.60/` 显示 JSON 而非前端页面
- 停旧容器、创建独立目录、上传部署配置与源码、构建镜像并启动 - **根因**: `@app.get("/")` 精确匹配抢占前端 SPA 回退路由 `@app.get("/{full_path:path}")`
- **修复**: `root()` 函数检查 `frontend/dist/` 是否存在,存在则返回 `index.html`,否则返回 JSON 健康检查
- **部署**: scp 上传 + 容器重启,已生效
- **验证**: `/` `/login` `/modules` `/cases` 全部 200 ✅;`/health` 正常 ✅
3. **数据迁移** ### 3. ✅ 数据库确认
- 7 张表全部迁移:modules(28) + test_cases(283) + executions(636) + case_results(926) + case_dependencies(0) + security_configs(1) + vulnerability_results(69)
4. **清理旧部署** - 生产环境(服务器):**MySQL 8.0**`plat_auto_test` 数据库,用户 `platapp`
- 已删除服务器旧目录 `~/ubains-module-test/deploy/` - 本地开发:**SQLite**
- 自动区分:`DATABASE_URL` 环境变量
### P1 卡点修复 — HeadlessChrome UA 伪装 ✅(本次会话新增) ---
- **根因**:容器内 Linux headless Chromium 的 UA 含 `HeadlessChrome`,被测站点据此返回不含登录表单的页面(`input:visible=0`),导致 `do_login()` 失败。
- **修复**`playwright_executor.py``start()` 中新增:伪装 UA(正常桌面 Chrome)、隐藏 `navigator.webdriver`、禁用 Blink 自动化特征标记、设置 locale/时区。
- **验证**:容器内 `do_login()` 返回 True,2 步用例 11.81s 执行通过。
- **状态**:已 scp 到服务器重启生效,**待 git commit 提交本地代码**
### 最终验证结果(全部通过) ## 三、最终验证结果
| 验收项 | 结果 | | 验收项 | 结果 |
|--------|------| |--------|------|
| 容器状态 | app + mysql 双容器 **healthy** | | 容器状态 | app + mysql 双容器 **healthy** |
| 前端访问 (http://192.168.5.60) | **HTTP 200** | | 前端首页 (http://192.168.5.60) | **HTML 200**(已修复,此前返回 JSON) |
| SPA 路由 (/login /modules /cases 等) | **HTTP 200**(回退 index.html) | | SPA 路由 (/login /modules /cases 等) | **HTTP 200**(回退 index.html) |
| API 路由 (/api/modules /api/cases /docs) | **HTTP 200** | | API 路由 (/api/modules /api/cases /docs) | **HTTP 200** |
| 健康检查 (/health) | `{"status":"healthy"}` | | 健康检查 (/health) | `{"status":"healthy"}` |
| 模块 / 用例数据 | **28 模块 / 283 用例** ✅ | | 模块 / 用例数据 | **28 模块 / 283 用例** ✅ |
| 8088 维护平台隔离 | **HTTP 302**(不受影响) | | git 提交 | `fd2c2f4d` 已推送 ✅ |
---
## 三、本次会话解决的 4 个关键问题(踩坑记录)
### 问题 1:MySQL 密码含 `@` 导致连接失败 🔴
- **现象**`Can't connect to MySQL server on '2026@mysql'`
- **根因**:密码 `PlatApp@2026` 中的 `@` 被 DATABASE_URL 当作密码/主机分隔符,主机名被解析为 `2026@mysql`
- **修复**:密码改为不含 URL 特殊字符的 `PlatApp2026`(改 `.env` + `docker-compose.yml` 默认值 + 迁移脚本)。改密码后**必须清理 `mysql/data/` 目录重新初始化**,否则旧密码用户残留 → Access denied
### 问题 2:ID 字段 `varchar(32)` 装不下实际 ID 🔴
- **现象**`Data too long for column 'id' at row 1`
- **根因**:模型 `String(32)`,但 `id_generator.py` 带前缀 ID(如 `module_432a65347d624b638f9623fe303af957`)有 39 字符。SQLite 不校验长度,MySQL 严格校验
- **修复**:所有 ID/外键字段 `String(32)``String(64)`(7 个 model 文件,15 处)
### 问题 3:外键约束导致迁移中途失败 🔴
- **现象**`foreign key constraint fails (modules.parent_id REFERENCES modules.id)`
- **根因**:自引用外键插入顺序问题。SQLite 默认不强制外键,MySQL 强制
- **修复**:迁移脚本迁移前 `SET FOREIGN_KEY_CHECKS=0`,完成后再开启
### 问题 4:aiomysql 与 pool_pre_ping 不兼容 → 部分 API 500 🔴
- **现象**`AsyncAdapt_aiomysql_connection.ping() missing 'reconnect'``/api/modules` 等 API 500
- **根因**:SQLAlchemy 的 `pool_pre_ping=True` 调 ping() 时未传 aiomysql 必需的 `reconnect` 参数
- **修复**`database.py` 中 MySQL 连接**禁用 `pool_pre_ping`**,改用 `pool_recycle=3600`;SQLite 仍保留 `pool_pre_ping`。按 DATABASE_URL 是否含 `sqlite` 自动判断
--- ---
## 四、卡点和问题 ## 四、卡点和问题
### 4.1 🟡 git commit 待办(P1 修复未提交) ### 4.1 🟡 根路径修复未提交
V2 部署变更已提交(commit `4cff19b6`)。但 **P1 修复(`playwright_executor.py` UA 伪装)尚未 commit**,目前只在服务器容器内生效(已 scp + restart)。下次首要:用 `/GitCommit` skill 提交此修复并推送。详见第六节「待提交变更」。
### 4.2 ✅ P1 卡点已解决(2026-07-28)— HeadlessChrome UA 伪装
**现象(修复前)**:V2 容器内创建 UI 用例执行后,执行记录永远停在 `status=running``passed/failed=0`,case_results 全 `pending`;最简单 2 步用例跑 180s 无结果;容器内出现僵尸 chrome 进程(State=Z),需 `docker compose restart app`复。 `backend/app/main.py``root()` 修改**尚未 git commit**,仅在服务器上生效(已 scp + restart)。下次首要:提交此修复。
**根因(已确认)****HeadlessChrome UA 被被测站点区别对待**。容器内 Linux headless Chromium 的 User-Agent 含 `HeadlessChrome` 字样,被测站点(192.168.5.44)据此返回了**不含登录表单**的页面 —— 现场实测 `input:visible=0``button:visible=0`,导致 `do_login()` 填表/点登录全部失败,返回 False,后续步骤每个都超时累积卡死。本地 Windows 因 UA 不同所以无法复现。 ### 4.2 ✅ P1 卡点 — 已解决并入库(2026-07-28 修复,2026-07-29 提交)
**修复(`backend/app/executors/playwright_executor.py` 的 `start()` 方法)** **根因**:容器内 Linux headless Chromium 的 UA 含 `HeadlessChrome`,被测站点据此返回不含登录表单的页面(`input:visible=0`),导致 `do_login()` 失败。
1. **启动参数新增**`--disable-blink-features=AutomationControlled``--disable-automation`(隐藏 `navigator.webdriver`)、`--disable-dev-shm-usage``--disable-gpu`(容器兼容)。
2. **`new_context()` 新增**`user_agent=`(伪装为正常桌面 Chrome `Chrome/126.0.0.0`,去掉 HeadlessChrome)、`locale="zh-CN"``timezone_id="Asia/Shanghai"`
3. **`add_init_script`**`Object.defineProperty(navigator, 'webdriver', {get: () => undefined})`(双保险隐藏 webdriver)。
**验证结果(容器内现场实测,修复前 → 修复后)** **修复内容**`playwright_executor.py``start()` 中:
1. 启动参数:`--disable-blink-features=AutomationControlled``--disable-automation``--disable-dev-shm-usage``--disable-gpu`
2. `new_context()`:伪装 UA(`Chrome/126.0.0.0`)、`locale="zh-CN"``timezone_id="Asia/Shanghai"`
3. `add_init_script`:隐藏 `navigator.webdriver`
| 检查项 | 修复前 | 修复后 | **验证**:容器内 2 步用例从「180s 无结果」→「11.81s 通过」✅
|--------|--------|--------|
| `navigator.userAgent` | 含 `HeadlessChrome` | 正常 Chrome UA ✅ |
| `navigator.webdriver` | `true` | `undefined` ✅ |
| `input:visible` | **0** | **3** ✅ |
| `button:visible` | **0** | **1** ✅ |
| `do_login()` 返回 | `False` | **`True`** ✅ |
| 登录后 URL | 停在登录页 | `/#/platform?...Home` ✅ |
**端到端验证**:在容器内用修复后的执行器完整跑「新建会议页面访问验证」用例(2 步:auto_login → 点击 `.block:has-text("新建会议")` → 等待 body),结果 **`status=passed`,总用时 11.81s**(修复前 180s 无结果)。**P1 彻底解决。** ### 4.3 Phase 5 剩余待办
**排查过程时间线** | 优先级 | 任务 | 状态 |
1. 前两次会话确认 do_login 在容器失败、本地成功 → 排除选择器/站点改版,根因收窄到容器环境差异,怀疑 HeadlessChrome UA。 |--------|------|------|
2. 本次会话:直接修改执行器 `start()` 加 UA 伪装 + 隐藏自动化特征,scp 上传 + restart app,容器内跑 `debug_ua_fix.py`(验证 do_login)和 `debug_ui_exec.py`(验证完整用例执行),两者均通过。 | 🔴 P0 | Settings.vue 系统配置页面 | ⏳ 待开始 |
| 🔴 P0 | Pinia 状态管理(executionStore / settingsStore) | ⏳ 待开始 |
**遗留小问题**:登录后立即查 `.block` 数量=0(本地是 29),疑似主页卡片异步加载,需稍等。但 do_login 的成功判断基于 URL + 登录表单消失,不受影响;后续真实步骤(如点击 `.block:has-text`)有 `wait_for_element` 容错,实测能通过。可后续观察,不影响 P1 解决结论。 | 🔴 P0 | 单元测试(后端 pytest + 前端 Vitest) | ⏳ 待开始 |
| 🟡 P1 | 数据库迁移 Alembic | ⏳ 待开始 |
**复用资源(本次新增)** | 🟡 P1 | 后台执行优化 | ⏳ 待开始 |
- `backend/scripts/debug_ua_fix.py` — 容器内验证 do_login + UA 伪装是否生效(打印 navigator.userAgent/webdriver、input:visible 数量)。 | 🟡 P1 | 前端布局抽取(Layout 组件) | ⏳ 待开始 |
- `backend/scripts/debug_ui_exec.py` — 容器内完整执行一个 2 步用例,端到端验证。
--- ---
<details> ## 五、未提交变更
<summary>📂 历史排查记录(折叠备查,P1 已解决,以下为排查过程)</summary>
**已验证排除的因素**(不是这些原因):
- ❌ Playwright 容器内能否跑:✅ 能。手动 `sync_playwright` 脚本可正常启动 chromium、导航 `https://192.168.5.44`,1.9s 完成。
- ❌ SSL 证书问题:✅ 执行器 `new_context(ignore_https_errors=True)` 正确。
- ❌ MySQL 连接:✅ 连接字符串正确,建执行记录、查用例数据都正常。
- ❌ 执行器 `start()`:✅ 容器内 `executor.start()` 成功。
- ❌ MySQL 并发锁:本次只跑 1 个用例仍卡死,与并发无关。
**本地诊断结论(关键转折)**:本地 Windows headless=True/False 均 `input:visible=3`、do_login 返回 True,排除选择器不匹配/站点改版,根因收窄到容器环境差异,最终确认为 HeadlessChrome UA。
</details>
### 4.3 前端热更新未实测
热更新机制已实现(volume 挂载 dist/),但未实际「改前端→build→上传→刷新」验证。**未遇到问题,仅未验证**,优先级低于 4.2。
---
## 六、本次变更清单(✅ 已提交 commit `4cff19b6`) ```
M backend/app/main.py # 根路径 / 修复(已部署到服务器,待 git commit)
### 修改的文件 ?? backend/app/__pycache__/ # ❌ Python 编译缓存
?? backend/__pycache__/ # ❌ 同上
| 文件 | 变更说明 | ?? backend/debug_*.png # ❌ 调试截图
|------|---------| ?? backend/scripts/debug_*.py # ❌ 调试脚本
| `backend/app/main.py` | 新增 StaticFiles 挂载前端 + SPA 路由回退(serve_spa) | ?? frontend/dist/ # ❌ 前端构建产物
| `backend/app/database.py` | 区分 SQLite/MySQL 连接参数,MySQL 用 pool_recycle 替代 pool_pre_ping | ?? frontend/node_modules/ # ❌ 前端依赖包
| `backend/app/models/module.py` | ID 字段 `String(32)``String(64)` | ```
| `backend/app/models/test_case.py` | ID/外键字段 `String(32)``String(64)` |
| `backend/app/models/execution.py` | ID 字段 `String(32)``String(64)` |
| `backend/app/models/case_result.py` | ID/外键字段 `String(32)``String(64)` |
| `backend/app/models/case_dependency.py` | ID/外键字段 `String(32)``String(64)` |
| `backend/app/models/security_config.py` | ID 字段 `String(32)``String(64)` |
| `backend/app/models/vulnerability_result.py` | ID/外键字段 `String(32)``String(64)` |
| `backend/requirements.txt` | 新增 `aiomysql==0.2.0` + `cryptography==42.0.0` |
| `deploy/docker-compose.yml` | 重写为 2 服务(app + mysql),密码改为 `PlatApp2026` |
| `deploy/.env.example` | 新增 MySQL 配置,密码改为 `PlatApp2026` |
| `deploy/deploy.sh` | 重写,适配新目录和新架构 |
| `CLAUDE.md` | 踩坑记录新增 #10-#13(MySQL 相关 4 条) |
### 新增的文件
| 文件 | 说明 |
|------|------|
| `deploy/Dockerfile` | 合一应用镜像(仅装环境,代码 volume 挂载) |
| `deploy/init.sql` | MySQL 初始化脚本 |
| `backend/scripts/migrate_sqlite_to_mysql.py` | SQLite → MySQL 数据迁移脚本 |
| `Docs/PRD/需求文档/部署运维/_PRD_..._V2部署架构升级_合一容器MySQL.md` | V2 需求文档 |
| `Docs/PRD/需求文档/部署运维/_PRD_..._合一容器MySQL_计划执行.md` | V2 执行计划 |
| `Docs/部署方案/Linux容器化部署方案_v2.md` | V2 设计方案 |
| `HANDOFF_V2部署升级.md` | V2 部署详细交接文档 |
### 删除的文件
| 文件 | 说明 |
|------|------|
| `deploy/Dockerfile.backend` | 旧后端 Dockerfile(已合并到合一 Dockerfile) |
| `deploy/Dockerfile.frontend` | 旧前端 Dockerfile(已合并) |
### P1 排查可复用的现场资源
| 资源 | 位置 | 用途 |
|------|------|------|
| `debug2.py` | 服务器 `/tmp/debug2.py` + 容器 `/app/debug2.py` | 硬编码用例测执行器(绕过 DB),确认 do_login 失败 |
| `debug3.py` | 服务器 `/tmp/debug3.py` + 容器 `/app/debug3.py` | 直接操作页面,打印登录页 input/button 数量与结构 |
| 登录页截图 | 容器 `/tmp/debug_login.png``/tmp/debug_login_after.png` | 用 `docker cp plat-auto-test-app:/tmp/debug_login.png ./` 拉取查看 |
| 现场测试用例 ID | `case_d4f98d82418b4b22a176da03922fbd66` | 最简单的 2 步 UI 用例(验证-登录测试) |
| 关键代码位置 | `backend/app/executors/playwright_executor.py:222``do_login`) | 登录逻辑,选择器待核对 |
---
## 七、关键踩坑记录(绝对不要重复踩) `main.py` 需要提交,其余为不应提交的临时文件。
| # | 现象 | 根因 | 正确做法 |
|---|------|------|---------|
| 1 | `NotImplementedError` / Sync API inside asyncio | Windows 默认 SelectorEventLoop 不支持子进程 | 同步 API + ProactorEventLoop + 线程池 |
| 2 | 第二次执行 `start()` 失败 | 共享执行器 `_is_running` 状态 | 每用例新建 PlaywrightExecutor 实例 |
| 3 | 请求 `/api/api/cases` 404 | `baseURL:'/api'` 与路径叠加 | baseURL 留空,路径写完整 `/api/...` |
| 4 | `SQLite database is locked` | 同步执行并发写入 | check_same_thread=False + pool_pre_ping |
| 5 | bash 中 `start` 启动 CMD 失败 | MSYS 封装损坏 `&&` 引号 | `MSYS_NO_PATHCONV=1 cmd.exe /c start ...` |
| 6 | `claude` 命令不在 PATH | Windows 未配全局 PATH | `which claude` 找路径,补 `.cmd` 后缀 |
| 7 | 前端页面空白 | `useRoute()` 在 computed 内调用 | 移到 setup 顶层 |
| 8 | `NOT NULL constraint failed: error_message` | 字段 nullable=False 但成功时为 None | 改为 nullable=True |
| 9 | Playwright Sync API inside asyncio loop | `run_in_executor` 线程中 Playwright 检测到 asyncio 循环 | `asyncio.set_event_loop(None)` 在工作线程中 |
| 10 | **MySQL `Can't connect on '2026@mysql'`** | DATABASE_URL 密码含 `@`,被解析为主机分隔符 | MySQL 密码不要含 `@` 等 URL 特殊字符(用 `PlatApp2026`) |
| 11 | **MySQL `Data too long for column 'id'`** | 模型 `String(32)` 容不下带前缀 ID,SQLite 不校验但 MySQL 校验 | ID 类字段统一用 `String(64)` |
| 12 | **MySQL 迁移 `foreign key constraint fails`** | 自引用外键插入顺序,SQLite 不强制但 MySQL 强制 | 迁移脚本先 `SET FOREIGN_KEY_CHECKS=0` |
| 13 | **MySQL `ping() missing 'reconnect'` → API 500** | aiomysql 的 ping() 与 SQLAlchemy `pool_pre_ping` 不兼容 | MySQL 禁用 `pool_pre_ping`,改用 `pool_recycle=3600` |
详见 `HANDOFF_V2部署升级.md` 第三节。
--- ---
## 、启动与运维指南 ## 、启动与运维指南
### 本地开发(仍用 SQLite,保持不变) ### 本地开发
```bash ```bash
# 后端(端口 8001) # 后端(端口 8001)
...@@ -281,37 +153,27 @@ npm install ...@@ -281,37 +153,27 @@ npm install
npm run dev npm run dev
``` ```
- `database.py` 自动按 DATABASE_URL 判断:含 `sqlite` 用 SQLite 配置,否则用 MySQL 配置 ### 服务器运维
- `main.py` 仅在 `frontend/dist/` 存在时挂载前端(本地 dev 时 Vite 代理处理)
### 服务器运维(V2 部署)
```bash ```bash
# 更新前端(热更新,无需重启) # 更新后端代码(需重启容器)
cd frontend && npm run build
scp -r dist/* ubains@192.168.5.60:/data/third_party/plat-auto-test/frontend/dist/
# 刷新浏览器即生效
# 更新后端(需重启容器,秒级)
scp -r backend/app ubains@192.168.5.60:/data/third_party/plat-auto-test/backend/ scp -r backend/app ubains@192.168.5.60:/data/third_party/plat-auto-test/backend/
ssh ubains@192.168.5.60 'cd /data/third_party/plat-auto-test/deploy && docker compose restart app' ssh ubains@192.168.5.60 'cd /data/third_party/plat-auto-test/deploy && docker compose restart app'
# 更新依赖(需重建镜像) # 更新前端(热更新,无需重启)
ssh ubains@192.168.5.60 'cd /data/third_party/plat-auto-test/deploy && docker compose build app && docker compose up -d app' cd frontend && npm run build
scp -r dist/* ubains@192.168.5.60:/data/third_party/plat-auto-test/frontend/dist/
# 数据迁移(本地执行,连接服务器 MySQL) # 查看日志
cd backend && python scripts/migrate_sqlite_to_mysql.py ssh ubains@192.168.5.60 'cd /data/third_party/plat-auto-test/deploy && docker compose logs -f app'
# 备份数据库 # 备份数据库
docker exec plat-auto-test-mysql mysqldump -uroot -pUbains@13579 plat_auto_test > backup.sql docker exec plat-auto-test-mysql mysqldump -uroot -pUbains@13579 plat_auto_test > backup.sql
# 查看日志 / 重启
ssh ubains@192.168.5.60 'cd /data/third_party/plat-auto-test/deploy && docker compose logs -f app'
``` ```
--- ---
## 、关键配置信息 ## 、关键配置信息
| 项目 | 值 | | 项目 | 值 |
|------|-----| |------|-----|
...@@ -321,30 +183,28 @@ ssh ubains@192.168.5.60 'cd /data/third_party/plat-auto-test/deploy && docker co ...@@ -321,30 +183,28 @@ ssh ubains@192.168.5.60 'cd /data/third_party/plat-auto-test/deploy && docker co
| 前端地址 | http://192.168.5.60 | | 前端地址 | http://192.168.5.60 |
| API 文档 | http://192.168.5.60/docs | | API 文档 | http://192.168.5.60/docs |
| 健康检查 | http://192.168.5.60/health | | 健康检查 | http://192.168.5.60/health |
| MySQL 外部访问 | 192.168.5.60:3307(仅调试) | | MySQL 外部访问 | 192.168.5.60:3307 |
| MySQL 用户/密码 | platapp / PlatApp2026 | | MySQL 用户/密码 | platapp / PlatApp2026 |
| MySQL root 密码 | Ubains@13579 | | MySQL root 密码 | Ubains@13579 |
| MySQL 数据库 | plat_auto_test | | MySQL 数据库 | plat_auto_test |
| 本地开发 | SQLite(http://localhost:3000 前端 / http://localhost:8001 API) | | 本地开发 | SQLite(http://localhost:3000 / http://localhost:8001) |
--- ---
## 、文档索引 ## 、文档索引
| 文档 | 路径 | | 文档 | 路径 |
|------|------| |------|------|
| 项目指南 | `CLAUDE.md` | | 项目指南 | `CLAUDE.md` |
| 多窗口并行开发指南 | `Docs/多窗口并行开发指南.md` | | 多窗口并行开发指南 | `Docs/多窗口并行开发指南.md` |
| V2 部署升级 PRD | `Docs/PRD/需求文档/部署运维/_PRD_需求文档_V2部署架构升级_合一容器MySQL.md` | | 主 PRD | `Docs/PRD/需求文档/总览/_PRD_平台自动化测试可视化系统需求文档.md` |
| V2 部署升级执行计划 | `Docs/PRD/需求文档/部署运维/_PRD_需求文档_V2部署架构升级_合一容器MySQL_计划执行.md` | | 当前进度记录 | `Docs/PRD/需求文档/总览/当前进度记录.md` |
| V2 设计方案 | `Docs/部署方案/Linux容器化部署方案_v2.md` | | 开发进度报告 | `Docs/PRD/需求文档/总览/_开发进度报告_20260709.md` |
| V2 部署设计 | `Docs/部署方案/Linux容器化部署方案_v2.md` |
| V2 部署详细交接 | `HANDOFF_V2部署升级.md` | | V2 部署详细交接 | `HANDOFF_V2部署升级.md` |
| UI 自动化交接 | `HANDOFF_UI自动化.md` | | UI 自动化交接 | `HANDOFF_UI自动化.md` |
| 安全测试交接 | `HANDOFF_安全测试.md` | | 安全测试交接 | `HANDOFF_安全测试.md` |
| 主 PRD | `Docs/PRD/需求文档/总览/_PRD_平台自动化测试可视化系统需求文档.md` |
| 当前进度记录 | `Docs/PRD/需求文档/总览/当前进度记录.md` |
| 本文档 | `HANDOFF.md` |
--- ---
*本文档由 Claude Code 于 2026-07-28 生成,记录 V2 部署架构升级会话进度,供下次会话快速恢复上下文。* *本文档由 Claude Code 于 2026-07-29 生成,记录 V2 部署架构升级 + 三期修复会话进度,供下次会话快速恢复上下文。*
\ No newline at end of file
...@@ -152,13 +152,17 @@ app.include_router( ...@@ -152,13 +152,17 @@ app.include_router(
@app.get("/", tags=["健康检查"]) @app.get("/", tags=["健康检查"])
async def root(): async def root():
""" """
根路径健康检查 根路径 — 容器部署时返回前端页面,否则返回健康检查 JSON
返回服务状态信息,用于确认服务正常运行。
Returns: Returns:
dict: 服务状态信息 FileResponse (前端 dist 存在时) 或 dict (健康检查)
""" """
# 容器部署时,前端 dist 存在,直接返回 index.html
FRONTEND_DIST = Path(__file__).parent.parent / "frontend" / "dist"
if FRONTEND_DIST.exists():
return FileResponse(str(FRONTEND_DIST / "index.html"))
# 本地开发时,返回健康检查 JSON
return { return {
"status": "ok", "status": "ok",
"message": f"{settings.APP_NAME} API 服务运行中", "message": f"{settings.APP_NAME} API 服务运行中",
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论