开发日志_2026-11-03.md 9.7 KB

2026-11-03 账号管理与权限配置

需求

「增加账号管理、权限配置的功能」—— 之前系统完全没有身份认证, 同一局域网任何人打开网址都能看全部数据、还能改阈值。

设计:三级角色

角色 查看 导出 导入 同步 改阈值/文案 账号管理
管理员 admin ✅ ✅ ✅ ✅ ✅ ✅
运营 editor ✅ ✅ ✅ ✅ ❌ ❌
查看 viewer ✅ ❌ ❌ ❌ ❌ ❌

实现

新增 backend/auth.py

  • hash_password():PBKDF2-HMAC-SHA256,12 万次迭代,每账号独立 16 字节盐
  • verify_password():hmac.compare_digest 防时序攻击
  • check_strength():至少 6 位、不能全是数字
  • 角色与权限表(ROLES / PERMS / GRANTS / can())
  • 会话令牌:secrets.token_urlsafe(32),库里只存 SHA-256 哈希

db.py 新增两张表

  • users:用户名、密码哈希、盐、角色、启用状态、是否需改密、最近登录
  • sessions:令牌哈希、用户、过期时间、IP;过期自动清理

server.py

  • 每个请求解析 sid Cookie → 还原当前用户
  • 路由权限表 PERM_RULES,未列出的 /api/* 默认只要登录
  • 未登录 → 401 need_login;权限不足 → 403 并说明当前角色
  • 新接口:/api/login、/api/logout、/api/me(GET/PUT)、/api/users(GET/POST/PUT/DELETE)、/api/auth/state
  • 未登录访问 / 直接给 login.html
  • 首次启动 ensure_admin() 创建 admin / admin123,标记 must_change=1
  • 保护规则:不能删除/停用自己;不能把最后一个管理员降级或停用;改密码会踢掉该账号已有会话

前端

  • 新增 login.html(首次使用会显示初始账号提示)
  • 顶部显示「当前账号 · 角色」+ 改密码 / 退出
  • 按权限隐藏左侧栏入口(指标配置、文案配置、账号管理、导入、同步、导出)
  • 登录后若 must_change 弹出不可关闭的改密框
  • 新增账号管理侧面板:新建、改角色、停用/启用、重置密码、删除、最近登录时间

验证

  • 未登录:/ → 登录页;/api/dashboard → 401
  • admin 登录 → must_change 改密框弹出,全部 6 个入口可见
  • viewer:看板 200,导出/改阈值/账号管理/同步/导入 全部 403
  • editor:看板 + 导出 + 同步 200,改阈值/账号管理 403
  • 改密:旧密码校验通过后生效,原密码随即失效;弱密码被拒("密码至少 6 位")
  • 账号面板:3 行正确渲染,角色下拉 3 个选项

踩坑

  1. import auth 没加进 server.py,登录报 name 'auth' is not defined
  2. /api/users 把 GRANTS 里的 set 直接序列化,_json 的 default=str 把集合转成了 "['view']" 这样的字符串,前端拿到的是字符数组 —— 改为 sorted(v)
  3. 权限测试时误触发了真实同步(POST /api/sync 返回 200 = 真跑了), 随后重启服务把同步打断,留下一个孤儿 ego 进程,已清理;数据未受影响

导出入口合并

问题

用户问:左侧栏【导出原始数据】跟【导入数据】里的导出是不是重复了。

结论

有重叠但不完全等价:

  • 左侧栏那个 = 当前周期最新一次导入的原件
  • 导入历史每行的按钮 = 指定批次的原件(同一周期导过多次时能区分)

2026-11-02 就有两个批次(01:04 和 21:32),左侧栏只能拿到最新那份。

处理

合并入口,左侧栏去掉【导出原始数据】(6 项 → 5 项),导入面板里新增:

  • 【下载当前周期最新一份】按钮(高频操作仍然一键)
  • 一段说明 + 每条历史记录的【原始文件】按钮(低频、需要选批次时用)

能力没有减少,只是入口收敛到「导入/导出是一对」的地方。

验证

  • 左侧栏现在 5 项:文案配置 / 指标配置 / 同步数据 / 导入数据 / 账号管理
  • 导入面板里「导出原始文件」区块和按钮正常,历史 9 条记录按钮齐全
  • 权限映射同步改为 btnExportCur,查看角色看不到这个按钮

新建账号改为一次性表单

反馈

「新建账号时,把所有要填的字段一次性列出,不要填完再跳下一个。角色用选择的,不要输入」

原来用三次 prompt() 依次问:用户名 → 密码 → 角色(还得手打 admin/editor/viewer,很容易打错)。

改动

新增通用表单弹窗 formModal({title, fields, validate, onSubmit}),新建账号一次列出 5 个字段:

字段 类型 说明
登录账号 文本 必填,限字母数字 _ . -,2~32 位
显示名称 文本 选填,不填用账号名
初始密码 密码 必填,≥6 位、不能全数字
确认密码 密码 必填,两次必须一致
角色 下拉选择 查看 / 运营 / 管理员,每项带一句说明

其他细节:

  • 错误提示在表单内就地显示,不弹 alert
  • 回车即提交,Esc/点遮罩可关闭
  • 「重置密码」也一并改成同样的表单(原来也是 prompt)

验证

  • 两次密码不一致 → 「两次输入的密码不一样」
  • 账号名含空格 → 「登录账号只能用字母、数字、_ . -,2~32 位」
  • 正常提交 → 弹窗关闭,列表立即出现「测试督导 test_dd」(已删除)

修关闭按钮 + 右上角退出登录

问题 1:账号管理面板的 × 点了没反应

closePanels() 里只写了 ['thPanel', 'imPanel', 'txPanel'],漏了 usPanel。 现在补上,同时左侧栏按钮的高亮状态也一并复位。

问题 2:退出登录不明显

原来「改密码 / 退出」是标题下方的一行小链接,藏在 hsub 里。

改成右上角一个账号区:

[ 近30天 · 截至 11-03(1416店)▾ ]  [ 临 临时验证/管理员  改密码  退出登录 ]
  • 头像取名字首字,金色圆角块
  • 名字 + 角色两行
  • 「退出登录」做成描边按钮,hover 变红
  • 头部改为 flex-wrap:nowrap + .hleft{flex:1},保证账号区始终在右上角 (窄屏 <1100px 时才折行)

验证

  • 点 × → 面板关闭(classList.contains('on') false)
  • 左侧栏按钮二次点击也能收起
  • 右上角账号区 rightTop=0, rightRight=0,与标题同一行

admin 账号设为内置账号

需求

「admin 账号的删除按钮要去掉,admin 账号是必须有的」

改动

  • 前端:admin 这一行不再显示【停用】【删除】,改为一个灰底说明「内置账号,不可停用/删除」
  • 后端:_user_delete / _user_update 直接拦截,返回 「admin 是内置账号,不能删除」/「不能停用」

验证

用另一个管理员账号调用接口:

  • DELETE /api/users/1 → admin 是内置账号,不能删除
  • PUT {"active":false} → admin 是内置账号,不能停用 账号列表不变,admin 仍启用。

部署到服务器时账号与数据会怎样(实测)

分别模拟了两种情况:

情况 A:只拷代码,不带 data/ 目录

  • 启动时自动创建内置账号 admin / admin123,并强制首次登录改密
  • 门店数据为空:0 家门店、0 个周期,需要先同步或导入

情况 B:整个目录拷过去(含 data/app.db)

  • 不会重新初始化,沿用原有的所有账号和密码 (实测:admin、lynch 都在;admin 的密码是改过的那个,admin123 已失效)
  • 全部历史数据都在:1416 家门店、5651 条指标、4 个周期、9 个批次
  • 原始文件也一起带过去(data/processed/),导出功能可用

结论

一次迁移请连同 data/ 一起拷;只要代码的话是干净的空白系统。 data/ 里包含:app.db(账号+指标)、processed/(原始文件)、inbox/、factory_thresholds.json。

服务器干净部署(方案 A)交付

用户选择

「只拷代码,不带 data 目录」—— 服务器上是一个全新的空白系统。

新增文件

文件 用途
start.sh Linux/服务器启动脚本,非交互、支持 PORT 环境变量和 --local
deploy/store-ops.service systemd 服务模板(自动重启、日志进 journal)
DEPLOY.md 完整部署说明:环境要求、上传、启动、systemd、端口放行、备份、排错

关键决策:包内文件名全部用 ASCII

第一次打包时中文文件名在 zip 里是乱码(unzip -l 显示 store-ops/�?��?�工�?�?�.bat), 和最早那个迁移包是同一个坑。服务器上乱码文件名会很难用,所以:

  • 门店检测工作台.service → store-ops.service
  • 部署说明.md → DEPLOY.md
  • 移除 启动工作台.bat、开放局域网访问.bat、启动工作台.command(服务器用不上)
  • 重打包后用 zipfile 校验:中文文件名 0 个

服务器上没有 ego-browser 的处理

自动同步依赖 ego-browser,服务器通常没有。原来会每天 01:00 报一次错。 现在启动时先检测:

 数据同步: 未启用(这台机器上没有 ego-browser)
           请用【导入数据】上传从生意经导出的 Excel

并且不再启动 daily_sync_loop 线程。实测(模拟 HOME/PATH 里没有 ego)通过。

验证(从 zip 全新解压跑一遍)

  • 解压后 21 个文件,文件名全部正常
  • 首次启动自动建 admin / admin123,强制改密
  • 登录成功,看板返回空数据(引导页正常)
  • 部署包 432KB,不含 data 目录

已知限制(已写进 DEPLOY.md)

服务器上没有自动同步,三种替代方案:手动导入 / 本机同步后上传 / 服务器装无头浏览器(不推荐)。