数据隔离
- settings / usage_records 主键改为 (user_id, key) / (user_id, request_id),
索引一律以 user_id 打头;collect_runs / audit_log 增加 user_id
- query / collect / scheduler 全链路把 uid 作为 conn 之后的第一个位置参数且无默认值
(漏传直接 TypeError,不会退化成「返回全量」)
- 配置三级回落 个人→实例→DEFAULTS;NO_FALLBACK_KEYS={cookie,user_agent} 不回落
凭证保密
- 新增 workbuddy_portal/crypto.py:手写 ChaCha20(RFC8439 §2.3) + HMAC-SHA256
encrypt-then-MAC,零第三方依赖;主密钥 cookie_key 与 SECRET_KEY 分键位存放
- get_secret() 是取明文的唯一通道;get_settings() 把加密键置空;
secret_state() 只回 {set,chars,tail,broken};升级时自动加密历史明文
注册与验证码
- 新增 /register 与 workbuddy_portal/captcha.py(手写 PNG + 点阵字模 + 干扰线)
- 验证码答案只存服务端表、不进 session,一次性、5 分钟过期、按 purpose 隔离
- allow_register / register_max_per_ip / captcha_policy / captcha_length 四个实例级开关
- 失败限速改为 IP + 用户名双维度;停用账号每请求回查、立即失效
页面
- 新增 /profile(个人中心)与注册页;登录页加验证码与自助注册入口
- /config 增加凭证状态、cookie_broken 告警、实例级设置区;/users 增加邮箱/状态与启停
修复
- base.html 顶层 {% set me %} 覆盖子模板同名变量,导致个人中心「注册于」渲染为空
- WB_COOKIE_SECURE 未写进 compose 的 environment,在 .env 里设了不生效
- 「修改登录密码」提示写「至少 6 位」,与实际策略(≥8 位 + 两类字符)不符
- 「用户管理」删除说明写「可勾选保留」,与页面实际行为不符
- 注册页与 flash 文案里的 **强调** Markdown 字面量
验证与文档
- smoke.py 99 → 165 项断言(多用户隔离 / 凭证保密 / 注册与验证码 / 3 条防回归)
- check_live.py 56 → 83 项断言(新增注册 / 验证码 / 安全响应头一节)
- demo_data.py 造两个账号;shots.py 自动过验证码、重出 11 张截图
- README / SECURITY / ARCHITECTURE / API / DEPLOYMENT / USER-GUIDE / FAQ / CHANGELOG / CONTRIBUTING 同步
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 + 角色(管理员 / 普通) |
| 凭证 | Cookie ChaCha20 + HMAC 静态加密入库,页面与接口只回掩码 |
| 防攻击 | 登录 / 注册图形验证码(服务端出题 + 一次性)、失败限速、注册限额 |
| 版本 | v1.2.0 |
| 许可证 | MIT(第三方组件与再分发资源见 THIRD-PARTY-NOTICES.md) |
目录:核心特性 · 架构 · 快速开始 · 命令一览 · 页面一览 · 接口一览 · 文档导航 · 安全须知 · 开源与许可
核心特性
| 能力 | 说明 |
|---|---|
| 多用户隔离 | 每个账号只填自己的 Cookie、收自己的数据、看自己的日志。user_id 是所有查询的第一个条件,且是必填位置参数(漏传直接 TypeError,不会静默返回全量) |
| 凭证加密 | Cookie 以 ChaCha20(RFC 8439)+ HMAC-SHA256 encrypt-then-MAC 密文入库;主密钥单独放在 data/instance.json,与 SECRET_KEY 分开。升级时会把历史明文自动加密 |
| 自助注册 + 验证码 | 开放注册(可关),登录/注册均带图形验证码。答案是服务端本地点阵渲染的 PNG,只存库、一次性、5 分钟过期——不进会话(Flask 会话是签名不加密的,放进去等于送答案) |
| 增量采集 | 按 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 │
│ │ ▲ (全部带 user_id) data/usage.sqlite(WAL)│
└───────────┼──────┘ ▲ │
client.py(urllib)│ ▲ crypto.py 解密本账号 Cookie │ │
│ └ captcha.py 出验证码图 │ │
│ query.py(uid 为第一个查询条件) │
│ ▲ ▲ │
│ web/views.py ──────┘ └──── web/api.py│
│ (Jinja 后台) (JSON) │
└───────────────┬───────────────────────────┬──────────────┘
▼ ▼
/ /records /tasks /config /logs /dashboard(ECharts 大屏)
/users /profile + 未登录:/login /register
五层职责:
| 层 | 位置 | 说明 |
|---|---|---|
| 采集 | workbuddy_portal/collect.py + scheduler.py |
纯 urllib 调云端;断点、去重、锁、导入导出,全部按 uid 隔离 |
| 存储 | workbuddy_portal/db.py + schema.sql |
SQLite WAL,单写者;运行期配置也在库里(settings 表,主键 (user_id, key)) |
| 加固 | workbuddy_portal/crypto.py + captcha.py |
凭证静态加密(零第三方依赖手写 ChaCha20);验证码用自写 PNG 编码器出图 |
| 聚合 | workbuddy_portal/query.py |
daily / dims / top / records / summary / bundle,全部下推 SQL,uid 是第一个条件 |
| 呈现 | workbuddy_portal/web/ |
Jinja 后台(views.py)+ JSON API(api.py)+ 静态大屏 |
为什么验证码不用 SVG、也不用第三方库:SVG 是文本,答案会明文出现在页面源码里; 而本项目坚持
requirements.txt只有 Flask / waitress / openpyxl,所以 PNG 编码器 (zlib 压缩 IDAT)与 5×7 点阵字模都是手写的,见 架构说明。
快速开始
方式一: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
从 v1.1.0 升级上来不需要手工介入:
init会检测到旧表结构并自动迁移 (历史数据归到首个账号、明文 Cookie 就地加密),全程带审计留痕,可重复执行。
第一次使用必做三件事
- 改密码——局域网可访问,默认密码等于没锁门(「个人中心」或「配置管理 → 修改密码」)。
- 填 Cookie——「配置管理 → 凭证」,否则采集只会记一条
cookie_expired。 获取方式见 用户手册。 - 确认调度时刻——「任务管理」里把
09:00,17:00改成你的习惯时刻,保存即生效。
想给同事开账号?
登录页底部有「自助注册」入口(管理员可在「配置管理 → 实例级设置」关掉)。 注册同样要过验证码,且同一来源每天最多注册 3 个账号(可改)。 每个账号登录后填自己的 Cookie——系统不会、也无法把某人的凭证给别人用。
只想内部开号、不开放注册?管理员在「用户管理」页直接新建即可; 命令行也行:
python manage.py passwd alice 强密码(默认普通账号,加--role admin提权)。
命令一览
统一入口是 manage.py(Docker 里同样可用:docker compose exec portal python manage.py stats)。
多用户下所有涉及数据/凭证的子命令都作用于某一个账号,用 -u/--user <用户名> 指定;
不指定则取「管理员优先、其次 id 最小」的那个(所以旧习惯的单账号用法仍然成立)。
唯独 collect 不带 -u 时会逐个启用账号跑一遍,与进程内调度线程的行为一致。
| 命令 | 作用 |
|---|---|
init |
初始化 / 迁移数据库(幂等)。--user / --password 指定首个管理员 |
serve |
启动 Web。--host --port --debug --no-scheduler |
collect [-u 账号] |
执行一次增量采集后退出;不带 -u 则所有启用账号各跑一次 |
migrate-csv [文件] [-u 账号] |
从旧版 CSV 存档导入(默认自动探测旧项目路径),必须说明「算谁的」 |
import-xlsx <文件> [-u 账号] |
合入官网「用量明细-导出」的 xlsx |
import-creds [-u 账号] |
从 VSCode / Cursor / Trae 的 settings.json 读取 codebuddyUsage.* 写入该账号 |
fill-prompt [-u 账号] |
回补缺失的 User Prompt(官网导出会丢约 22%) |
export-csv [路径] [-u 账号] |
导出 CSV(默认 data/exports/usage_records_<账号>.csv,文件名带归属) |
vacuum |
wal_checkpoint(TRUNCATE) + VACUUM,回收空闲页、压缩 WAL |
stats [-u 账号] |
先全库概览(每账号多少条 / 多少积分 / Cookie 状态),再给指定账号的维度明细 |
users |
列出所有账号:角色、状态、数据量、凭证状态、最近登录 IP |
status |
逐账号显示调度开关 / 下次执行 / Cookie 状态 / 最近采集 |
passwd <用户> [新密码] |
重置或创建账号;--role admin 提权,--activate 顺手启用 |
自检工具
| 脚本 | 层 | 说明 |
|---|---|---|
tools/smoke.py |
离线回归 | test_client 对真实库全页面只读渲染,165 项断言:历史缺陷防回归 ①~⑭、多用户隔离 / 凭证保密 / 注册与验证码全链路、CSV 列、class↔CSS 对账、静态资源逐个 200。不需要先起服务 |
tools/check_live.py |
真实 HTTP | 对运行中的服务走真实链路(登录 → CSRF → 各页面 → 各 API → 导出 → 安全项 → 验证码与响应头),83 项断言,基本只读 |
tools/shots.py |
界面实检 | Playwright 登录后逐页截图并收集 console / pageerror,产物在 data/shots/ |
tools/demo_data.py |
示例数据 | 生成完全合成的示例库(两个账号,各有自己的数据与假 Cookie),文档截图基于它 |
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审计行,并临时建两个普通账号用于验证权限边界与注册链路 (无论成败都在finally里删掉),不动任何用量数据;check_live.py/shots.py只读,但登录成功会更新users.last_login_at/login_count。 两者在验证码策略为always时会从本地库里取答案以完成自动登录 ——取的是会话里的 captcha id(答案本身只存在于服务端)。
页面一览
| 路径 | 作用 |
|---|---|
/login |
登录(未登录时的落点):用户名 / 密码 / 图形验证码,底部有自助注册入口 |
/register |
自助注册:用户名、显示名、邮箱、密码 + 验证码;注册成功直接登录并引导去填自己的 Cookie |
/ |
概览:KPI(含今日 vs 昨日整日)、采集健康度、调度状态、模型 TOP、最近采集 |
/dashboard |
ECharts 交互大屏(独立静态页):日历热力图、趋势、维度分布、单笔 TOP,支持区间/维度/指标联动 |
/records |
数据明细:快捷区间、日期/模型/客户端/关键词筛选、排序、分页、展开 Prompt、导出 CSV |
/tasks |
任务管理:调度开关与时刻、启动补跑、按区间补采、运行历史 |
/config |
配置管理:自己的 Cookie / UA、采集参数、TLS 校验、维护动作;底部是实例级设置区(仅管理员可改) |
/logs |
日志管理:只看得到自己账号的逐次采集详情(含 [warn]/[error] 原文)、操作审计;应用日志尾部仅管理员 |
/profile |
个人中心:账号概况、我的凭证状态(密文入库)、改密码;点右上角用户名进入 |
/users |
用户管理(仅管理员):新建账号、改显示名/权限/状态/密码、删除、账号操作审计 |
其余页面截图见 用户手册。
接口一览
全部需要登录(/api/* 未登录返回 401 JSON);写接口另需 CSRF(请求头 X-CSRF-Token,
页面已注入 window.WB_CSRF)。
所有数据接口都只返回当前登录账号的数据 —— user_id 由会话决定,不接受客户端传入。
完整参数说明见 docs/API.md。
| 方法 | 路径 | 作用 |
|---|---|---|
| GET | /api/manifest |
存档总量、日期区间、存活日清单、数据源、健康状态(含 cookieChars/cookieBroken) |
| 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> |
明细分页 / 单条详情(<id> 也受 user_id 约束) |
| GET | /api/runs · /api/runs/<id> |
采集运行历史 / 单次详情(含逐行日志) |
| GET | /api/status |
调度状态、下次执行、互斥锁、最近采集 |
| GET | /api/audit |
操作审计分页 + 可选动作清单(管理员看全站,普通账号看自己) |
| POST | /api/collect |
手动触发采集(可指定区间补采);未配 Cookie 回 409 no_cookie,密文解不开回 409 cookie_broken |
| POST | /api/maintenance/<action> |
fill-prompt | export-csv | vacuum | recount(vacuum 仅管理员) |
| GET/POST | /api/settings |
读 / 写配置。读只回掩码 cookie_hint;非管理员写实例级键会被拒(400 + denied 清单) |
| POST | /api/profile |
改自己的显示名 / 邮箱 |
| POST | /api/password |
修改自己的登录密码 |
| POST | /api/captcha |
验证码机制自述(策略、位数、TTL、图片地址),便于排障自检 |
| GET/POST | /api/users · /api/users/<id> · /api/users/<id>/delete |
用户管理(仅管理员) |
| GET | /captcha.png?purpose=login|register |
图形验证码图片(唯一无需登录的接口;每次都是新题,带 no-store) |
| 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 离线回归(165 项断言)
│ ├── check_live.py 真实 HTTP 验收(83 项断言)
│ ├── shots.py Playwright 逐页截图 + JS 报错收集
│ └── demo_data.py 生成合成示例库(两个账号)
└── workbuddy_portal/
├── __init__.py create_app:配置 / 日志 / 蓝图 / 错误页 / 启动调度
├── config.py 路径、项目标识、默认值、写时校验、密钥管理
├── db.py SQLite 连接、schema、按作用域读写配置、账号、审计
├── schema.sql 表结构(多用户布局)
├── crypto.py 凭证静态加密(手写 ChaCha20 + HMAC-SHA256)
├── captcha.py 图形验证码(手写 PNG 编码器 + 点阵字模)
├── security.py 密码哈希、会话、CSRF、失败限速、验证码策略、角色、响应头
├── client.py 云端接口(urllib)+ 编辑器凭证读取
├── collect.py 增量采集 / 去重入库 / 互斥锁 / xlsx 导入 / CSV 导出
├── scheduler.py 进程内调度线程(按账号遍历 + 槽位去重 + 启动补跑)
├── query.py SQL 聚合层(uid 必填)
└── web/
├── views.py 页面路由(含 /login /register /captcha.png /profile)
├── api.py JSON API
├── templates/ base / login / register / profile / 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),不要共用管理员。 - 数据按账号隔离:
uid是所有查询的必填位置参数(漏传直接报错,不会静默返回全量);/api/runs/<id>、/api/records/<id>这类按 id 取的单条接口也带user_id约束; 应用日志尾部仅管理员可看。 - Cookie 静态加密:ChaCha20 + HMAC-SHA256(encrypt-then-MAC)密文入库,主密钥在
data/instance.json的cookie_key(与SECRET_KEY分开,轮换代价不同)。get_settings()把加密键一律置空,要明文只有db.get_secret()一条路—— 这样任何「顺手打印全部配置」的代码都带不出凭证。升级时历史明文会被自动加密。 - Cookie 不跨账号回落:
NO_FALLBACK_KEYS(cookie/user_agent)不参与实例级回落, 否则新账号会「继承」管理员的凭证,属于最严重的串号越权。 - 验证码先于口令校验:登录时先验验证码再比密码,避免攻击者拿「密码对不对」当信号,
在解验证码之前就把字典跑完。答案存服务端
captchas表,一次性、5 分钟过期、按用途隔离, 下发到浏览器的只有随机 id(Flask 会话是签名不加密的,放答案等于送答案)。 - 注册受双重限制:验证码 + 同 IP 每日配额(默认 3 个,可改;
allow_register=0可整体关闭)。 - 停用账号立即失效:
current_user()每个请求回查users.status,不必等 12 小时会话过期。 - CSRF 全站校验,退出登录也是
POST(GET 型退出能被<img src="/logout">静默触发)。 - 开放重定向防护:登录跳转的
next只接受站内相对路径,//evil.com这类协议相对 URL 一律回落到/。 - 登录限速:按 IP 与用户名两个维度分别计数,任一维度连续失败 5 次即锁 10 分钟; 失败计数表有上限与 TTL;验证码出图另有 60 秒 40 张的限速(不设限就是一条廉价的 CPU 放大路径)。
- 安全响应头:CSP(
frame-ancestors 'none')、X-Frame-Options: DENY、nosniff、Referrer-Policy: same-origin、COOP;/api/*与/captcha*带Cache-Control: no-store。 - 不进版本库的文件:
data/instance.json(含secret_key与cookie_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 为通用示例文本,
Cookie 是 deadbeef… / cafef00d… 这类一眼可辨的假串,
审计 IP 取自 RFC 5737 的文档专用网段(192.0.2.0/24)。
生成方式是 tools/demo_data.py(会造 admin 与 demo 两个账号,各有自己的数据),
所以任何人不需要真实账号就能复现整套文档。截图由 tools/shots.py 逐页重出(11 张,
含注册页与个人中心),脚本会读示例库里的验证码答案自动过掉登录。
重出截图时请用相对路径起示例服务(
WB_DATA_DIR=data/demo):用绝对路径会让启动日志 印出C:\Users\<用户名>\…,而那一行正好会出现在「日志管理」页的截图上。
致谢
- 交互大屏依赖 Apache ECharts
- Web 框架 Flask,生产服务器 waitress
- 行为准则框架来自 Contributor Covenant
