wangchuanli 23799b4ea5 feat(oss): 补齐开源声明体系(MIT + 第三方声明 + 贡献/安全/行为准则)
* LICENSE              —— MIT
  * THIRD-PARTY-NOTICES  —— 依赖清单、再分发合规说明(含随仓库分发的
                            Apache ECharts 5.6.0 / Apache-2.0)与自查清单
  * CONTRIBUTING.md      —— 开发环境、五层验证、必须遵守的不变量、提交规范
  * SECURITY.md          —— 漏洞私有报告渠道、已有措施、已知非目标
  * CODE_OF_CONDUCT.md   —— 改编自 Contributor Covenant 2.1
  * .github/             —— Bug 报告 / 功能建议表单 + PR 模板
  * .editorconfig        —— 与 .gitattributes 保持一致
  * 全部 Python / Shell 源文件加 SPDX-License-Identifier: MIT 头
  * README 增加「开源与许可」章节与许可标识
2026-09-14 16:15:26 +08:00

WorkBuddy Portal

workbuddy-portal —— WorkBuddy 积分用量「采集 / 存储 / 呈现」一体化门户

一个独立部署的 Python / Flask 应用:把账号云端的用量明细按时采集下来、按 request_id 去重存档,再以「管理后台(配置 / 任务 / 日志 / 明细)+ ECharts 交互大屏」两种形态呈现。 不依赖任何外部计划任务或自动化——调度线程就跑在 Web 进程里。

语言 / 框架 Python 3.11+ · Flask 3 · Jinja2 · 纯标准库 urllib 采集
存储 SQLite(WAL),单文件正本 data/usage.sqlite
前端 服务端渲染后台 + 独立 ECharts 大屏(离线自带的 echarts.min.js)
部署 Docker Compose / 裸机 waitress;镜像可推 Gitea 容器注册表
鉴权 全站登录 + CSRF + 角色(管理员 / 普通用户),凭证存库、页面只回掩码
版本 v1.1.0
许可证 MIT(第三方组件与再分发资源见 THIRD-PARTY-NOTICES.md)

目录:核心特性 · 架构 · 快速开始 · 命令一览 · 页面一览 · 接口一览 · 文档导航 · 安全须知 · 开源与许可


核心特性

能力 说明
增量采集 按 MAX(ts) 断点续采 + 回退窗口;主键 ON CONFLICT 去重,冲突时以「更早的本地时间」为准
进程内调度 每天固定时刻(默认 09:00,17:00)由内置线程触发;支持启动补跑(程序没开时错过的时刻,开机后在宽限期内补上)
单写者保证 文件锁 data/collect.lock 让「调度 / 页面手动触发 / CLI」三处不并发写 SQLite;僵尸锁 30 分钟可抢占
全量存档 不随官网导出窗口过期而丢数据;官网 xlsx 丢失约 22% 的 Prompt,可用 fill-prompt 回补
大屏去中间层 大屏直接走 /api,按当前筛选窗口实时聚合;左侧多取等长一段用于算环比,窗口不变不重复请求
可观测 每次采集落一条 collect_runs(含 [warn]/[error] 逐行原文);另有操作审计与登录审计
一键备份 正本就是宿主机上的一个 .sqlite 文件,拷走即可;manage.py vacuum 回收空闲页

架构一图

                    ┌──────────────── workbuddy-portal(单进程)────────────────┐
  云端用量接口       │                                                          │
  /billing/meter/   │  scheduler.py ──┐                                        │
  get-user-request- │  (20s 轮询槽位)  │                                        │
  usage             │                 ▼                                        │
        ▲           │  collect.py ─ 文件锁 collect.lock ─ 去重 upsert ─▶ SQLite  │
        │           │      ▲                              data/usage.sqlite(WAL) │
        └───────────┼──────┘                                     ▲               │
   client.py(urllib)│                                          │               │
                    │                        query.py(聚合全部下推 SQL)        │
                    │                              ▲            ▲              │
                    │            web/views.py ──────┘            └──── web/api.py│
                    │            (Jinja 后台)                       (JSON)    │
                    └───────────────┬───────────────────────────┬──────────────┘
                                    ▼                           ▼
                          /  /records /tasks            /dashboard(ECharts 大屏)
                          /config /logs /users

四层职责:

层 位置 说明
采集 workbuddy_portal/collect.py + scheduler.py 纯 urllib 调云端;断点、去重、锁、导入导出
存储 workbuddy_portal/db.py + schema.sql SQLite WAL,单写者,运行期配置也在库里(settings 表)
聚合 workbuddy_portal/query.py daily / dims / top / records / summary / bundle,全部下推 SQL
呈现 workbuddy_portal/web/ Jinja 后台(views.py)+ JSON API(api.py)+ 静态大屏

快速开始

方式一:Docker Compose(推荐)

git clone https://git.iwali.top/wangchuanli/workbuddy-portal.git
cd workbuddy-portal

cp .env.example .env            # 至少设好 WB_ADMIN_PASSWORD
# 编辑 .env: WB_ADMIN_PASSWORD=一个足够强的密码

docker compose up -d --build
docker compose logs -f          # Ctrl-C 退出日志跟踪,容器继续跑

打开 http://<本机IP>:8848 → 用 .env 里设的账号登录 → 去「配置管理」粘贴 Cookie。

数据与日志放在 Docker 命名卷(workbuddy-portal_wb_data / _wb_logs)里, docker compose down 不会删。要用 CLI 就 docker compose exec portal python manage.py …。 不要在宿主机上跑 manage.py 去连容器的库——Windows + Docker Desktop 的 9p 挂载下, 宿主进程碰一次 WAL 库就会让容器打不开数据库(纯读也会触发,且不自愈)。 想直接看到数据/日志,用 docker-compose.hostdir.yml 叠加层(仅建议 Linux 宿主机)。 详见 部署与运维指南。

方式二:裸机 Python

pip install -r requirements.txt

python manage.py init                 # 建表 + 默认配置 + 管理员 admin/admin123
python manage.py import-creds         # 可选:把编辑器设置里的 cookie/UA 接管进数据库
python manage.py migrate-csv          # 可选:把旧版 CSV 存档全量导入
python manage.py serve                # 启动,默认 0.0.0.0:8848

第一次使用必做三件事

  1. 改密码——局域网可访问,默认密码等于没锁门(「配置管理 → 修改密码」)。
  2. 填 Cookie——「配置管理 → 凭证」,否则采集只会记一条 cookie_expired。 获取方式见 用户手册。
  3. 确认调度时刻——「任务管理」里把 09:00,17:00 改成你的习惯时刻,保存即生效。

命令一览

统一入口是 manage.py(Docker 里同样可用:docker compose exec portal python manage.py stats)。

命令 作用
init 初始化数据库(幂等)。--user / --password 指定首个管理员
serve 启动 Web。--host --port --debug --no-scheduler
collect 执行一次增量采集后退出(不想开 Web 时可挂系统计划任务)
migrate-csv [文件] 从旧版 CSV 存档导入(默认自动探测旧项目路径)
import-xlsx <文件> 合入官网「用量明细-导出」的 xlsx
import-creds 从 VSCode / Cursor / Trae 的 settings.json 读取 codebuddyUsage.* 写入数据库
fill-prompt 回补缺失的 User Prompt(官网导出会丢约 22%)
export-csv [路径] 导出与官网 xlsx 同构的 CSV(默认 data/exports/)
vacuum wal_checkpoint(TRUNCATE) + VACUUM,回收空闲页、压缩 WAL
stats 存档概况 + 模型维度表 + 最近采集(不联网)
status 调度开关 / 下次执行 / Cookie 状态 / 最近采集
passwd <用户> [新密码] 重置或创建登录账号

自检工具

脚本 层 说明
tools/smoke.py 离线回归 test_client 对真实库全页面只读渲染,99 项断言(历史缺陷防回归 ①~⑭、CSV 列、class↔CSS 对账、静态资源逐个 200),不需要先起服务
tools/check_live.py 真实 HTTP 对运行中的服务走真实链路(登录 → CSRF → 各页面 → 各 API → 导出 → 安全项),56 项断言,基本只读
tools/shots.py 界面实检 Playwright 登录后逐页截图并收集 console / pageerror,产物在 data/shots/
python tools/smoke.py                                          # 离线,随时可跑
python manage.py serve --port 8849 --no-scheduler              # 另开一个终端
python tools/check_live.py --base http://127.0.0.1:8849        # 真实 HTTP
python tools/shots.py    --base http://127.0.0.1:8849 --full   # 逐页截图

smoke.py 会写少量 audit_log 审计行(被拒的配置写入也留痕),不动业务数据; check_live.py 只读,但登录成功会更新 users.last_login_at / login_count。


页面一览

路径 作用
/ 概览:KPI(含今日 vs 昨日整日)、采集健康度、调度状态、模型 TOP、最近采集
/dashboard ECharts 交互大屏(独立静态页):日历热力图、趋势、维度分布、单笔 TOP,支持区间/维度/指标联动
/records 数据明细:快捷区间、日期/模型/客户端/关键词筛选、排序、分页、展开 Prompt、导出 CSV
/tasks 任务管理:调度开关与时刻、启动补跑、按区间补采、运行历史
/config 配置管理:Cookie / UA、采集参数、TLS 校验、修改密码、维护动作(回补 Prompt / 导出 / 整理库)
/logs 日志管理:逐次采集详情(含 [warn]/[error] 原文)、状态筛选、应用日志、操作审计
/users 用户管理(仅管理员):新建账号、改显示名/权限/密码、删除、用户操作审计

概览

其余页面截图见 用户手册。


接口一览

全部需要登录(/api/* 未登录返回 401 JSON);写接口另需 CSRF(请求头 X-CSRF-Token, 页面已注入 window.WB_CSRF)。完整参数说明见 docs/API.md。

方法 路径 作用
GET /api/manifest 存档总量、日期区间、存活日清单、数据源、健康状态
GET /api/bundle 大屏一次取齐:全量 daily + 窗口 dims/top/records/totals
GET /api/summary KPI + 环比(前一段不在存档内则不给假数字)
GET /api/daily 逐日聚合(含每日分模型、24 时段)
GET /api/dims 模型 / 客户端 / 时段汇总
GET /api/top 单笔消耗榜(唯一带 Prompt 摘要的接口)
GET /api/records · /api/records/<id> 明细分页 / 单条详情
GET /api/runs · /api/runs/<id> 采集运行历史 / 单次详情(含逐行日志)
GET /api/status 调度状态、下次执行、互斥锁、最近采集
GET /api/audit 操作审计分页 + 可选动作清单
POST /api/collect 手动触发采集(可指定区间补采)
POST /api/maintenance/<action> fill-prompt | export-csv | vacuum | recount
GET/POST /api/settings 读 / 写配置(非法值 400 并列出全部错误)
POST /api/password 修改自己的登录密码
GET/POST /api/users · /api/users/<id> 用户管理(仅管理员)
GET /logs/tail · /records/export 应用日志尾部 / 按筛选流式导出 CSV

目录结构

workbuddy-portal/
├── manage.py                       统一 CLI(唯一入口)
├── requirements.txt
├── Dockerfile                      多阶段构建(依赖层 / 运行层)
├── docker-compose.yml               单服务编排(数据放 Docker 命名卷)
├── docker-compose.hostdir.yml       可选叠加层:改用宿主机目录(仅建议 Linux)
├── .env.example                     环境变量样例
├── docker/
│   ├── entrypoint.sh               幂等初始化 → exec serve(LF 行尾)
│   └── healthcheck.py              标准库健康检查(免登录页 /login)
├── docs/                           文档(见下)
├── tools/
│   ├── smoke.py                    离线回归(99 项断言)
│   ├── check_live.py               真实 HTTP 验收(56 项断言)
│   └── shots.py                    Playwright 逐页截图 + JS 报错收集
└── workbuddy_portal/
    ├── __init__.py                 create_app:配置 / 日志 / 蓝图 / 错误页 / 启动调度
    ├── config.py                   路径、项目标识、默认值、写时校验
    ├── db.py                       SQLite 连接、schema、settings 读写、审计
    ├── schema.sql                  表结构
    ├── security.py                 密码哈希、session、CSRF、失败限速、safe_next、角色
    ├── client.py                   云端接口(urllib)+ 编辑器凭证读取
    ├── collect.py                  增量采集 / 去重入库 / 互斥锁 / xlsx 导入 / CSV 导出
    ├── scheduler.py                进程内调度线程(槽位去重 + 启动补跑)
    ├── query.py                    SQL 聚合层
    └── web/
        ├── views.py                页面路由
        ├── api.py                  JSON API
        ├── templates/              base / login / overview / tasks / config / logs / records / users / error
        └── static/
            ├── css/app.css         统一设计令牌
            ├── js/app.js           带 CSRF 的请求、表单与维护动作绑定
            ├── favicon.svg
            └── dashboard/index.html  ECharts 大屏(独立页)

文档导航

文档 面向 内容
docs/USER-GUIDE.md 使用者 用户使用手册:登录、各页面操作、Cookie 获取、导出、常见操作
docs/DEPLOYMENT.md 运维 部署与运维:Docker、裸机、反向代理、备份恢复、升级回滚、推镜像到 Gitea、排错
docs/ARCHITECTURE.md 开发 架构与设计说明:数据模型、调度与锁、聚合边界、安全模型、设计取舍
docs/API.md 开发 / 集成 接口参考:路径、参数、返回结构、错误码
docs/FAQ.md 所有人 常见问题:采集为空、Cookie 失效、时区、性能、权限
docs/CHANGELOG.md 所有人 变更日志
CONTRIBUTING.md 贡献者 贡献指南:开发环境、验证分层、必须遵守的不变量、提交规范
SECURITY.md 运维 / 安全 安全策略:漏洞私有报告渠道、已有措施、已知非目标
THIRD-PARTY-NOTICES.md 合规 第三方组件清单与许可证(含随仓库再分发的 ECharts)
CODE_OF_CONDUCT.md 所有人 行为准则
LICENSE 所有人 MIT 许可证全文

安全须知

局域网可访问 ⇒ 以下每一条都必要:

  • 必须改默认密码;给只读同事发普通账号(is_admin=0),不要共用管理员。
  • Cookie 就是账号凭证:只以掩码回显,存库不外传;默认开启 TLS 证书校验(ssl_verify=1), 仅在自签 / 企业代理场景临时关闭。
  • CSRF 全站校验,退出登录也是 POST(GET 型退出能被 <img src="/logout"> 静默触发)。
  • 开放重定向防护:登录跳转的 next 只接受站内相对路径,//evil.com 这类协议相对 URL 一律回落到 /。
  • 登录限速:同 IP 连续失败 5 次锁定 10 分钟;失败计数表有上限与 TTL。
  • 不进版本库的文件:data/instance.json(含 secret_key)、data/usage.sqlite、logs/、.env(含明文密码)。

开源与许可

本项目以 MIT 许可证发布,全文见 LICENSE。你可以自由使用、修改、商用与再分发, 只需保留版权声明与许可声明。

第三方组件(务必看一眼)

用量大屏随仓库再分发了 Apache ECharts 5.6.0(workbuddy_portal/web/static/dashboard/vendor/echarts.min.js, Apache-2.0 许可)——之所以内置而不走 CDN,是为了让大屏在局域网内离线可用。 按 Apache-2.0 第 4 条,再分发时需保留其许可证与版权声明(该文件头部已自带)。 其余运行期依赖(Flask / waitress / openpyxl)不在本仓库内,由使用方安装时获取。

完整的依赖清单、许可证对照表与合规自查清单见 THIRD-PARTY-NOTICES.md。

参与贡献

  • 想改代码?先读 CONTRIBUTING.md —— 里面有必须遵守的几条不变量 (SQLite 单写者、列表接口不回 prompt 全文、两种字段命名契约不要互相「统一」……), 以及从 compileall 到容器验证的五层自检该怎么跑。
  • 有想法但手上没有真实数据?python tools/demo_data.py 会生成一份完全合成的示例库, 写到 data/demo/(已在 .gitignore 内),可直接拿来调试界面与截图。
  • 发现安全漏洞?请不要开公开 Issue,按 SECURITY.md 走私有渠道。
  • 参与本项目即表示你同意遵守 CODE_OF_CONDUCT.md。

文档里的数据都是合成的

docs/images/ 的全部界面截图与 docs/ 中的 JSON 示例均为合成数据: 模型名统一为 demo-*,客户端为 vscode / webconsole / sdk,Prompt 为通用示例文本, 审计 IP 取自 RFC 5737 的文档专用网段(192.0.2.0/24)。 生成方式是 tools/demo_data.py,所以任何人不需要真实账号就能复现整套文档。

致谢

S
描述
workbuddy-portal —— WorkBuddy 积分用量「采集 / 存储 / 呈现」一体化门户
自述文档 MIT
20 MiB
语言
Python 70.4%
HTML 23.1%
CSS 3.2%
JavaScript 1.5%
Shell 1.3%
其它 0.5%