文件
workbuddy-portal/docs/CHANGELOG.md
T
wangchuanli 86631ae7ab chore: 项目定名为 workbuddy-portal,容器化并补齐文档体系
## 项目定名
- 目录 wb_usage_portal → workbuddy-portal
- Python 包 wb_usage → workbuddy_portal(含 session cookie 名)
- 界面品牌统一为 WorkBuddy Portal;项目标识收敛到 config 单一来源

## 容器化
- Dockerfile:多阶段构建,依赖层与源码解耦;非 root(uid 1000);内置健康检查
- docker-compose.yml:单服务 + 绑定挂载 data/logs + 日志轮转 + TZ
- docker/entrypoint.sh:幂等初始化 → exec serve(LF 行尾,已由 .gitattributes 锁定)
- docker/healthcheck.py:纯标准库探活 /login(slim 镜像无 curl)
- .dockerignore / .env.example;数据目录可用 WB_DATA_DIR 等环境变量覆盖

## 文档
- docs/USER-GUIDE.md    用户使用手册(含 9 张真实界面截图)
- docs/DEPLOYMENT.md    部署运维(Docker / 裸机 / 反代 / 备份 / 推 Gitea 注册表)
- docs/ARCHITECTURE.md  架构与设计说明(含已知坑与红线、验证体系)
- docs/API.md           接口参考(路径 / 参数 / 返回结构 / 错误码)
- docs/FAQ.md           常见问题;docs/CHANGELOG.md 变更日志

## 修复缺陷(8)
1. /records/export 必然 500:生成器在请求上下文销毁后才迭代,改用自建连接
2. 大屏页图表全白:相对路径把 echarts.min.js 解析成 /vendor/... → 404
3. /users 500:路由已注册但模板缺失
4. 明细页日期筛选失效:视图传 f.frm、模板读 f.from
5. 配置页维护按钮全死:调用了不存在的 WBU.bindMaint()
6. 审计只能看最近 40 条:LIMIT 写死
7. 明细页多跑一条无用 SELECT:day_list() 取了没人用
8. 登录页锁定阈值未从配置注入

## 安全加固
- 新增 safe_next():拒绝 //evil.com 等协议相对 URL 的开放重定向
- 缺 CSRF 的写请求统一 400
- 默认开启云端 HTTPS 证书校验(ssl_verify=1);Cookie 是账号凭证
- 登录失败计数表加上限与 TTL
- /logout 拆分为 POST(执行) + GET(仅提示),防 <img src=/logout> 静默退出
- settings 内部簿记键 slot:* 读写两侧过滤,不再从 /api/settings 泄漏

## 内部质量与工具
- 设置项写时校验 + 读时兜底,杜绝「一个手滑的数字让采集整个跑不起来」
- 全局 ValueError → 400:手写 query string 不再暴露 500 页面
- CSV 导出改 csv.writer 流式写入(原手工拼串,字段含逗号会串列)
- bundle 明细加 20000 上限并回传 recordsTotal/recordsTruncated,不静默丢数据
- tools/smoke.py 离线回归 99 项;tools/check_live.py 真实 HTTP 56 项
- tools/shots.py Playwright 逐页截图 + JS 报错收集

## 验证
- compileall 通过;smoke 99/99;对容器实例 check_live 56/56;截图 0 JS 报错
- 容器内采集实测成功(trigger=startup 补跑:新增 11 条)
2026-09-14 14:55:50 +08:00

6.9 KiB
原始文件 Blame 文件历史

变更日志

本项目遵循 语义化版本:主版本.次版本.修订号。

  • 主版本:不兼容的变更(数据库迁移需手工介入、接口契约变化)
  • 次版本:向后兼容的功能新增
  • 修订号:向后兼容的缺陷修复

[1.1.0] — 2026-09-14

主题:项目定名 workbuddy-portal · 容器化 · 文档体系

新增

  • Docker 化:多阶段 Dockerfile(依赖层与运行层分离,改业务代码不触发重装依赖)、 docker-compose.yml(单服务、数据绑定挂载、健康检查、日志轮转)、 docker/entrypoint.sh(幂等初始化 → exec 交接)、docker/healthcheck.py(纯标准库)、 .dockerignore、.env.example
  • 容器环境变量:WB_HOST WB_PORT WB_DATA_DIR WB_LOG_DIR WB_DB WB_ADMIN_USER WB_ADMIN_PASSWORD WB_DISABLE_SCHEDULER WB_IMPORT_CREDS WB_IMPORT_XLSX
  • 文档体系 docs/: 用户使用手册(含 9 张界面截图)、 部署与运维指南(含推镜像到 Gitea 注册表的完整流程)、 架构与设计说明、 接口参考、 常见问题
  • .gitattributes:强制 *.sh / Dockerfile / 各类源码为 LF (带 CRLF 的 .sh 在容器里会报 exec format error,极难定位)

变更

  • 项目定名:wb_usage_portal → workbuddy-portal; Python 包 wb_usage → workbuddy_portal;会话 cookie wb_usage_sid → workbuddy_portal_sid(升级后需要重新登录)
  • 界面品牌统一为 WorkBuddy Portal(此前为「WorkBuddy 用量门户」)
  • 项目标识收敛到 config.PROJECT_NAME / PROJECT_TITLE / PROJECT_DESC 单一来源, 模板通过 app_name 等上下文变量引用,不再多处硬编码
  • 数据 / 日志目录支持环境变量覆盖(WB_DATA_DIR / WB_LOG_DIR / WB_DB)
  • 版本号 1.0.0 → 1.1.0
  • 大屏页标题改为「WorkBuddy Portal · 积分消耗大屏」

修复

# 症状 根因
1 /records/export 必然 500 生成器在请求上下文销毁后才被迭代,复用 db.get_db() 撞「数据库已关闭」。改为生成器内自建连接
2 大屏页图表全白 /dashboard 无尾斜杠,src="vendor/echarts.min.js" 被解析成 /vendor/… → 404
3 /users 500 路由已注册但 users.html 不存在
4 明细页日期筛选失效 视图传 f.frm、模板读 f.from;导出链接拼 ?frm= 而接口只认 from
5 配置页 3 个维护按钮全死 模板调 WBU.bindMaint(),app.js 里没有该函数
6 审计只能看最近 40 条 LIMIT 40 写死
7 明细页多跑一条无用 SELECT day_list() 取了没人用
8 登录页锁定阈值写死 未从配置注入

#2 值得单独一提:前两层测试都没抓到(离线断言只看状态码 + <html>, 真实 HTTP 只看状态码),是加上 Playwright 截图后才发现的。 据此给 tools/smoke.py 补了「页面所有 src/href 资源引用逐个断言 200」一节。

安全

  • 新增 security.safe_next():登录跳转的 next 拒绝 //evil.com(协议相对 URL)、 /\evil.com、绝对地址与含 CR/LF 的值
  • 缺 CSRF 的写请求统一 400(此前部分路径漏检)
  • 默认开启云端 HTTPS 证书校验(ssl_verify=1)。Cookie 是账号凭证,不该裸奔
  • 登录失败计数表加上限(4096 个 IP)与 TTL(1 小时),防内存被大量来源 IP 撑爆
  • /logout 拆分为 POST(执行)+ GET(仅提示),防 <img src="/logout"> 静默退出
  • settings 的内部簿记键(slot:*)读写两侧都过滤,不再从 /api/settings 泄漏

内部质量

  • 设置项写时校验 + 读时兜底:config.normalize_setting()(范围 + 单位,非法值 400 并列出全部错误)+ 采集路径全面改用 db.get_int(),杜绝「一个手滑的数字让采集整个跑不起来」
  • 全局 ValueError → 400 处理器:手写 query string 不再能把 500 页面暴露出去
  • 日期归一化 norm_day() / norm_window()(容忍 2026/09/08、带时间、起止写反)
  • CSV 导出改用 csv.writer 流式写入(此前手工拼字符串,model/client 含逗号会串列)
  • bundle 明细加下限(BUNDLE_RECORDS_CAP = 20000),并返回 recordsTotal / recordsCap / recordsTruncated,不静默丢数据
  • 轮询日志尾部改用 collections.deque(maxlen=n),不再把整个文件读进内存
  • 修掉一条非法 CSS 声明 font: 13px/1.5 inherit(简写里 inherit 不能当字族, 整条被浏览器丢弃,输入框一直用默认字体)

工具

  • 新增 tools/smoke.py:离线回归 99 项断言(全页面只读渲染 + 模板残留 + 历史缺陷防回归 ①~⑭ + 静态资源逐个 200 + CSV 列 + 页面 class ↔ app.css 选择器对账), 不需要先起服务
  • tools/check_live.py 扩充到 56 项断言,新增用户管理 / 审计 / 流式导出 / 开放重定向 / CSRF 四节
  • 新增 tools/shots.py:Playwright 登录后逐页截图 + 收集 console / pageerror (内建可执行文件探测,规避驱动与本机浏览器版本错位)

[1.0.0] — 2026-09-14

主题:从「脚本 + CSV + 静态大屏」演化为独立可部署的门户

新增

  • 独立 Flask 应用:create_app 工厂 + views / api 双蓝图
  • SQLite(WAL)作为唯一数据正本,主键去重、断点续采、聚合全部下推 SQL
  • 进程内调度线程(20 秒轮询 + 槽位去重 + 启动补跑),不再依赖外部计划任务
  • 单写者文件锁 data/collect.lock(含 30 分钟僵尸锁抢占)
  • 登录鉴权(pbkdf2:sha256:200000)、全站 CSRF、同 IP 失败限速
  • 后台页面:概览 / 数据明细 / 任务管理 / 配置管理 / 日志管理
  • 独立 ECharts 交互大屏 /dashboard(离线自带 echarts,不依赖 CDN)
  • CLI:init serve collect migrate-csv import-xlsx import-creds fill-prompt export-csv stats status passwd vacuum
  • 从旧版 CSV / 官网 xlsx / 编辑器设置导入的迁移通道
  • 从 VSCode / Cursor / Trae 的 settings.json 接管 Cookie 与 User-Agent

变更

  • 数据正本从 CSV 改为 SQLite;CSV 降级为导出物(data/exports/)
  • 采集从外部脚本改入 Web 进程;删除全部外部自动化与计划任务
  • 运行期配置(Cookie、调度、采集参数)从文件搬进数据库,由后台页面维护

版本对照

版本 数据正本 调度 部署 鉴权
1.1.0 SQLite(WAL) 进程内 Docker Compose / 裸机 登录 + CSRF + 角色
1.0.0 SQLite(WAL) 进程内 裸机 登录 + CSRF
< 1.0 CSV 文件 外部计划任务 脚本 无(仅局域网)