diff --git a/README.md b/README.md index 505e421..d9363fd 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ ├── legacy/classic/ # 旧版前端备份(index.html / style.css / app.js) ├── weread.db # SQLite 数据库(运行 fetch_data.py 后生成) ├── requirements.txt # Python 依赖 +├── docs/troubleshooting.md # 日常维护与故障排查 ├── templates/ │ └── index.html # 「阅间」前端页面 └── static/ @@ -71,6 +72,8 @@ DEPLOY_BRANCH=dev ./deploy.sh 脚本会拉取指定分支,自动创建 `.venv`,安装 `requirements.txt`,执行数据库迁移,然后重启 `weread.service`。GitHub Actions 配置在 `.github/workflows/deploy.yml`,默认监听 `dev` 分支。 +运行端口、Python 环境、HTTPS 证书和接口故障的诊断方法见 [日常维护与故障排查](docs/troubleshooting.md)。 + ## 功能模块 | 模块 | 说明 | diff --git a/docs/superpowers/plans/2026-09-14-maintenance-debug-guide.md b/docs/superpowers/plans/2026-09-14-maintenance-debug-guide.md new file mode 100644 index 0000000..4e245ba --- /dev/null +++ b/docs/superpowers/plans/2026-09-14-maintenance-debug-guide.md @@ -0,0 +1,39 @@ +# Maintenance Debug Guide Implementation Plan + +> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 建立可持续维护的日常排障文档,记录本次端口与 Python CA 证书故障。 + +**Architecture:** `docs/troubleshooting.md` 保存完整故障记录;`README.md` 和 `docs/technical-guide.md` 仅提供入口与同步维护要求。运行态只保留当前项目的 8080 服务。 + +**Tech Stack:** Markdown、Git、macOS/Linux 常用诊断命令 + +--- + +### Task 1: 新增排障记录 + +**Files:** +- Create: `docs/troubleshooting.md` + +- [ ] 记录故障现象、根因链路、诊断命令、恢复步骤和预防措施。 +- [ ] 明确接口、端口、Python 环境或部署方式变化时同步更新本文档。 + +### Task 2: 增加文档入口 + +**Files:** +- Modify: `README.md` +- Modify: `docs/technical-guide.md` + +- [ ] 在 README 项目结构中加入排障文档。 +- [ ] 在技术指南中增加日常维护与排障入口及维护约束。 + +### Task 3: 验证并合并 + +**Files:** +- Verify: `docs/troubleshooting.md` +- Verify: `README.md` +- Verify: `docs/technical-guide.md` + +- [ ] 检查文档链接与 Markdown 格式。 +- [ ] 确认 8770 已关闭,8080 页面和状态接口返回 200。 +- [ ] 提交专门分支,并使用 `git merge --no-ff` 合并到 `dev`。 diff --git a/docs/technical-guide.md b/docs/technical-guide.md index ee77ee5..1e4fbd8 100644 --- a/docs/technical-guide.md +++ b/docs/technical-guide.md @@ -140,6 +140,12 @@ node --check static/js/weapi.js 真实数据库验证需先执行迁移,再显式运行 `WEREAD_TEST_MYSQL=1 python3 -m unittest discover -s tests -p 'test_reading_goals_mysql.py' -v`。测试创建唯一测试目标并清理,只操作本次生成的 ID。 +## 日常维护与故障排查 + +端口冲突、请求失败、Python 环境、HTTPS 证书和 systemd 服务的诊断步骤统一维护在 [日常维护与故障排查](troubleshooting.md)。接口路径、监听端口、Python 或 `.venv` 选择、启动脚本、systemd 配置以及部署流程发生变化时,必须同步更新该文档的运行基线;新故障按日期追加记录并保留历史结论。 + +正式服务只保留一个监听端口。首页可以打开但 API 失败时,应分别检查 `/api/reading/status`、`/api/reading/shelf` 和 `/api/reading/dashboard`,根据 404、502 或超时定位到路由、上游服务或 TLS 环境,不能只用首页 200 作为启动成功标准。 + 本次样式收紧覆盖正式看板的概览、书架和阅读目标页。顶栏、标题区、筛选区、统计卡、书架网格、右侧目标摘要、目标介绍区和目标卡片均使用覆盖规则压缩占位;书架在窄屏固定两列,避免横向溢出。本地真实数据验收覆盖 1280 px 桌面和 390 px 手机宽度:桌面顶栏 58 px,统计卡从约 200 px 开始,书架一行 7 本,目标页介绍区约 67 px 高,目标卡约 317 px 宽;手机顶栏 56 px,书架两列,目标卡约 298 px 宽,概览、书架和目标页均未出现横向溢出。 ## dev 自动部署 diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..e704cab --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,147 @@ +# 日常维护与故障排查 + +本文档记录正式看板的运行基线、常用诊断方法和已确认故障。接口路径、监听端口、Python 环境、启动脚本、systemd 服务或部署流程发生变化时,必须同步更新本文档;出现新的线上或本地故障时,按日期追加记录,不覆盖历史结论。 + +## 运行基线 + +| 项目 | 当前约定 | +|---|---| +| 本地正式看板端口 | `8080` | +| 当前 API 前缀 | `/api/reading/*` | +| 兼容 API 前缀 | `/api/v2/*`,仅用于旧客户端过渡 | +| 项目 Python | 优先使用项目根目录 `.venv/bin/python` | +| 服务器服务 | `weread.service`,端口以服务中的 `PORT` 为准 | +| 服务器目录 | `/opt/weread-board/WeReadBoard` | + +同一运行环境只保留一个正式服务实例。不要同时使用 `start_server.sh` 和 `start_dashboard.sh` 启动不同端口,否则静态文件、后端路由和 Python 环境可能来自不同进程,容易产生误判。历史静态 Demo 需要临时预览时可以单独启动,用完后应及时停止。 + +## 快速排查 + +页面出现“请求失败”时,按下面的顺序检查。 + +### 检查监听端口 + +本机: + +```sh +lsof -nP -iTCP -sTCP:LISTEN +``` + +服务器: + +```sh +sudo ss -ltnp +sudo systemctl status weread.service --no-pager +``` + +确认正式服务只监听预期端口,并核对进程的启动时间与 Python 路径: + +```sh +ps -fp +lsof -a -p -d cwd +``` + +### 分层检查接口 + +本机设置了 HTTP 代理时,必须绕过代理访问本地地址: + +```sh +curl --noproxy '*' -i http://127.0.0.1:8080/ +curl --noproxy '*' -i http://127.0.0.1:8080/api/reading/status +curl --noproxy '*' -i 'http://127.0.0.1:8080/api/reading/shelf?tab=all&progress=0' +curl --noproxy '*' -i 'http://127.0.0.1:8080/api/reading/dashboard?period=week' +``` + +- 页面返回 200、API 返回 404:运行中的后端没有加载当前路由,检查部署提交与服务重启。 +- API 返回 502:路由已经加载,但数据库或微信读书上游调用失败,查看响应正文和服务日志。 +- 请求一直等待:检查上游网络、DNS、TLS 证书和接口超时,不要只修改前端路径。 + +服务器日志: + +```sh +sudo journalctl -u weread.service -n 100 --no-pager +``` + +## 2026-09-14:接口调整后页面请求失败 + +### 现象 + +- `http://127.0.0.1:8080/` 可以打开,但页面显示“请求失败”。 +- `/api/reading/shelf` 和 `/api/reading/dashboard` 返回 502。 +- 旧的 `8770` 进程仍能通过 `/api/v2/status` 返回数据。 + +### 根因 + +接口调整只修改了 Flask 路由名称和前端请求前缀,微信读书 HTTPS 请求逻辑没有变化。真正差异来自服务重启后选择了另一套 Python: + +- 旧 `8770` 进程使用 WorkBuddy Python,其默认 CA 文件为 `/private/etc/ssl/cert.pem`。 +- 新 `8080` 进程因项目没有 `.venv`,由 `start_server.sh` 选择系统 Python。 +- 该系统 Python 的默认 CA 文件为空,访问 `https://i.weread.qq.com` 时出现 `CERTIFICATE_VERIFY_FAILED`。 + +完整因果链路如下: + +```text +接口调整需要重启服务 +-> 启动脚本选择了不同的 Python +-> 新 Python 无法找到 CA 证书 +-> 微信读书 HTTPS 请求失败 +-> 聚合接口返回 502 +-> 前端显示“请求失败” +``` + +因此,接口调整是触发条件,不是 HTTPS 故障本身。兼容路径只能处理旧进程返回 404 的过渡状态;同一后端因上游 TLS 失败而返回 502 时,切换 `/api/reading/*` 或 `/api/v2/*` 都不能解决问题。 + +### 诊断证据 + +检查 Python 与默认 CA 文件: + +```sh +python3 -c "import sys, ssl; print(sys.executable); print(ssl.get_default_verify_paths())" +``` + +故障环境中,`cafile` 为 `None`,且 Python 预期的证书文件不存在。系统证书 `/etc/ssl/cert.pem` 可用,使用它重新启动后,状态、书架和概览接口均恢复为 200。 + +### 恢复方式 + +本机临时恢复: + +```sh +SSL_CERT_FILE=/etc/ssl/cert.pem PORT=8080 python3 app.py +``` + +服务器由 systemd 管理时,先确认服务器证书文件的实际路径;仅在 Python 默认 CA 路径缺失时,为 `weread.service` 配置 `SSL_CERT_FILE`,然后执行: + +```sh +sudo systemctl daemon-reload +sudo systemctl restart weread.service +``` + +不要关闭 TLS 校验,也不要在代码中使用不验证证书的 SSL Context。 + +### 预防与验收 + +1. 部署和本地启动统一使用项目 `.venv/bin/python`,不要让不同启动脚本静默选择不同解释器。 +2. 重启前检查 Python 的 `ssl.get_default_verify_paths()`,确认 CA 文件存在且可读。 +3. 每次只保留一个正式服务端口,停止旧实例后再验收新实例。 +4. 接口、端口、Python 环境、启动脚本、systemd 配置或部署方式变化时,同步更新本文档的“运行基线”和相关故障记录。 +5. 验收必须同时检查页面、`status`、`shelf` 和 `dashboard`,不能只确认首页返回 200。 + +本次本地恢复后的验收结果:正式服务仅监听 `8080`,页面及上述三个 API 均返回 200;旧 `8770` 实例和 `8766` 静态预览均已停止。 + +## 故障记录模板 + +新增故障时使用以下结构: + +```markdown +## YYYY-MM-DD:故障标题 + +### 现象 + +### 根因 + +### 诊断证据 + +### 恢复方式 + +### 预防与验收 +```