kczx-user-management 1.0.4 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -72,6 +72,7 @@ node scripts/ensure-deps.mjs # 仅当上述解析不到 sche
72
72
  `/user-management/api/setup` 拒绝一切后续调用。
73
73
 
74
74
  > 登录页样式与 dsh-passwords 一致(光晕背景 + 网格 + 玻璃卡片 + 渐变按钮),跟随宿主主题深浅色,右上角可切换中文 / English。
75
+ > 品牌可配:标题 / 副标题 / 页脚 / LOGO / 主色 / 默认语言都在 `user-management:` 段里,见「配置」一节。
75
76
 
76
77
  ## 它做什么
77
78
 
@@ -99,15 +100,21 @@ node scripts/ensure-deps.mjs # 仅当上述解析不到 sche
99
100
 
100
101
  | 台账 | 内容 |
101
102
  |---|---|
102
- | 登录记录 | 登录 / 失败 / 登出 / 改密 / 初始化(首次建号)/ 建号 / 删号 / 角色变更 / 资料变更 / 权限变更(含紧凑差异串) |
103
- | 访问记录 | 页面级访问(保留真实客户端 IP) |
104
- | 操作日志 | 经过网关的每次 API 调用与 WebSocket 连接(方法 / 路径 / 状态 / 来源 IP) |
103
+ | 登录记录 | 登录 / 失败 / 登出 / 改密 / 初始化(首次建号)/ 建号 / 删号 / 角色变更 / 资料变更 / 权限变更(含紧凑差异串);按页浏览 |
104
+ | 访问记录 | 页面级访问(保留真实客户端 IP);按页浏览 |
105
+ | 操作日志 | 经过网关的每次 API 调用与 WebSocket 连接(方法 / 路径 / 状态 / 来源 IP);**对话类请求连内容一起记**:`session.prompt` 的提示词、`session.rename` 的会话标题写进同一行(最多 500 字,点开展开;粘贴的图片只记数量、不存 base64)。列表多一列「对话内容」,并按页浏览(默认每页 50,可选 20/50/100/200) |
105
106
 
106
107
  另有 IP 封禁(回环与当前 IP 拒绝封禁,防自锁)与 TOTP 两步验证(默认关闭,可选开启)。
107
108
 
109
+ **分页**:登录记录 / 访问记录 / 操作日志 / 用户表四张表都是服务端分页——查询参数带 `limit` + `offset`,
110
+ 响应回 `{ ..., total }`,前端底部是共用的分页控件(上一页 / 第 X/Y 页 · 共 N 条 / 下一页 / 每页 20·50·100·200,
111
+ 默认 50)。筛选同样在服务端执行,所以「共 N 条」永远是**匹配总数**而不是当前页条数。账号表只有在调用方
112
+ 传了 `limit` 时才分页,不传仍然整份返回(后端脚本的老用法不受影响)。
113
+
108
114
  ## 配置
109
115
 
110
- 走 `~/.dsh/settings.yaml` 的 `user-management:` 段(改完要么热重载、要么重启):
116
+ 走 `~/.dsh/settings.yaml` 的 `user-management:` 段。改完即时生效的字段无需重启;只有监听器字段
117
+ (`listenHost` / `port` / `plaintext` / `sites`)会触发监听器重建,其余一律下一个请求就用新值。
111
118
 
112
119
  ```yaml
113
120
  user-management:
@@ -116,11 +123,90 @@ user-management:
116
123
  port: 8088
117
124
  plaintext: false # true 时只允许回环(明文服务观察层用,见 MERGED-DEPLOYMENT.md)
118
125
  sites: [] # 空 = 自动枚举本机 IP;配域名 + 证书则按 SNI 选择
119
- title: 'DSH 控制台'
120
126
  setupKey: '' # 首次初始化密钥;留空 = 启动时随机生成并写到 setup-key.txt
121
127
  historyMaxBytes: 67108864 # 历史改写缓冲上限(字节,默认 64 MiB,最小 1 MiB)
128
+
129
+ # ── 登录页品牌(全部即时生效,改完刷新 /login 即可) ──
130
+ title: 'DSH 控制台' # 部署名:标签页标题后缀 + 标题兜底
131
+ brandName: '' # 卡片主标题;只填它,门面就是你的产品名
132
+ loginTitle: '' # 逐行覆盖内置文案('' = 用内置的「登录」/ Sign in)
133
+ loginSub: ''
134
+ setupTitle: '' # 同上,作用于首次「初始化平台」表单
135
+ setupSub: ''
136
+ footerText: '' # '' = 内置的「dsh 插件 · user-management」
137
+ showFooter: true
138
+ logoUrl: '' # http(s)、data: URI,或本机图片文件;'' = 内置标记
139
+ logoSize: 48 # 标记方框边长(px,24–96);图片按 contain 铺满方框
140
+ auditConversationText: true # 操作日志记录 session.prompt 文字与会话标题(见下)
141
+ brandColor: '' # '#rrggbb';只重染品牌色 token,danger/ok/warn 不动
142
+ defaultLang: 'zh' # 不带 ?lang= 时的门面语言(zh | en)
143
+ showLangSwitch: true # 是否显示页内中英切换
144
+
145
+ # ── 会话与请求策略(全部即时生效) ──
146
+ sessionDays: 7 # 会话有效期(天,滑动续期:过半后续一次)
147
+ maxBodyBytes: 65536 # 本插件 API 的 JSON 请求体上限(字节)
148
+ otpFailLimit: 5 # 两步验证连续错误几次后锁定
149
+ otpLockoutSeconds: 60 # 锁定多久
122
150
  ```
123
151
 
152
+ > 登录页的品牌只吃 `brandName` / `loginTitle` 这类字段,**改名不会影响账号与台账**;`brandColor` 只重染
153
+ > 品牌色那几个 token(按钮、LOGO 底、聚焦环及其阴影),状态色保持不变——颜色承载语义,不该被顺手改掉。
154
+ >
155
+ > **LOGO 支持本机图片**(1.1.2 起):`logoUrl` 填绝对路径(`E:/brand/logo.png`)或 `$DSH_HOME/user-management/`
156
+ > 下的文件名(把图片放到 users.json 旁边,然后填 `logo.png`)。裸路径本身不是 URL,浏览器会拿它当相对路径去
157
+ > 请求网关,所以本机图片由插件用 `/user-management/branding/logo` 这条**公开**路由回吐给登录页(和 /login 一样
158
+ > 必须免登录,否则图片是 401);该路由只吐这一个配置文件,且限定图片扩展名、非空、≤ 512 KiB,其余一律 404,
159
+ > 路径下的其它子路径在本机就直接 404,不会被匿名转发到 dsh 上游。改图不用重启——路由每次现读文件。
160
+ >
161
+ > 图片**铺满方框**:`logoSize` 控制方框边长(默认 48px,24–96),图片用 `object-fit: contain` 填进去——方图正好
162
+ > 铺满,长图/竖图留白而不裁切。这是品牌字段里唯一会改布局的一个,调大小在设置页里改完刷新即可,不用重启。
163
+ > 未接线的占位字段仍有 `loginFailLimit` / `lockoutSeconds`(登录失败不自动锁定,用 IP 封禁代替),
164
+ > 保留只是为了老的 `settings.yaml` 继续可解析。
165
+
166
+ ### 操作日志的分页(1.3.0 起)
167
+
168
+ 台账上限 5000 行、每行还带对话文字,一次全拉进浏览器既慢又没必要。现在「操作日志」是**服务端分页**:
169
+ `GET /user-management/api/audit?limit=50&offset=100` 返回 `{ entries, total, limit, offset }`——除了这一页,
170
+ 还给出**匹配总数**,前端才说得出「第 3/40 页 · 共 1987 条」。表格下方有上一页 / 下一页和每页条数(20/50/100/200)。
171
+
172
+ 两个容易做错的点,都按服务端口径处理:
173
+
174
+ - **筛选也走服务端**,和分页窗口是同一组参数:`username` / `method` / `path` / `statusClass`。若在前端过滤已取回的
175
+ 那一页,翻页翻的就是"某个结果的子集",而且"共 N 条"只能是这一页的数字——那是错的。筛选条件改动会**回到第 1 页**
176
+ (换条件后停在旧页码没有意义),输入框有 250ms 去抖,不会每敲一个字发一次请求。
177
+ - **删掉最后一条**时若当前页只剩那一行,会退回上一页,而不是停在"第 N 页(空的)"。
178
+
179
+ ### 操作日志里的对话内容(1.2.0 起)
180
+
181
+ 「用户管理 → 操作日志」每一行是经过网关的一次 API 调用。以前只看得到 `POST /api/session.prompt`——可这一行不论用户说了什么都长一个样,
182
+ 日志于是回答不了「这个账号到底做了什么」。现在网关把人打的那句话从请求体里取出来,随行写进台账:
183
+
184
+ - `session.prompt` → `content[]` 里所有 `type: 'text'` 片段按行拼接;`type: 'image'` 只记**数量**(截图是 base64,不是台账该存的东西)。
185
+ - `session.rename` → 会话标题。
186
+ - 其它 RPC 完全不碰请求体,仍按原来的方式流式转发。
187
+ - 存储侧每行最多 500 字(`MAX_AUDIT_TEXT`,超长截断);前端那一列默认单行省略、点一下就地展开全文,图片数量显示为徽标。
188
+ - **只观察、不拦截**:抓取只是顺路把请求体缓一下再原样重放给上游,字节不变;超过 2 MiB 的请求体(例如带大图的提示词)原样透传、不记录文字。
189
+ - 隐私开关:`user-management.auditConversationText: false`(设置页「会话与请求策略」里也有这一项)关掉即完全不抓。台账是
190
+ `$DSH_HOME/user-management/audit.jsonl` 明文文件,提示词可能含口令 / 密钥时请关闭。
191
+
192
+ ### 在设置页里改(1.1.0 起)
193
+
194
+ 上面这些字段也可以在 **设置 → 通用设置** 里改,就在「语言」「外观」那几行下面:「登录页与策略配置」一行,
195
+ 默认收起着(通用列里都是紧凑的单行偏好,十六个字段全展开会把这一页压垮),行内摘要写着登录页当前显示
196
+ 什么、有几项被改过,点「展开」出现完整表单。本插件在浏览器侧把这个设置命名空间绑成
197
+ `settingsScope.bind({ namespace: 'user-management' })`,并注册进 `settings.general.item` 槽——宿主注册
198
+ 命名空间、浏览器注册控件,两边经由同一份 settings 文档同步。
199
+
200
+ 表单行为:改过没存的字段标「未保存」;写入过 `settings.yaml` 的字段标「已自定义」并带「恢复默认」
201
+ (`unset`,清掉覆盖回到内置值,也让 `settings.yaml` 保持干净);文本框留空等于清除覆盖;数字字段按 schema
202
+ 的下限校验(比如请求体上限 ≥ 1024);保存按字段顺序逐个写入,**任何一个值非法就整体不落盘**并提示是哪一项。
203
+ 远程浏览器访问时设置是只读的(settings RPC 只走回环),这一行会直接说明并让你改服务器上的 `settings.yaml`;
204
+ 装了旧版插件时它也会明说「这些新字段保存了也不会生效」。
205
+
206
+ **刻意没放进设置页的字段**:`listenHost` / `port` / `plaintext` / `sites` / `enabled` 会重启监听器——那正是承载
207
+ 这个设置页的东西,从 UI 里翻可能把自己关在门外;`trustedSecret` / `setupKey` 是密钥,不该经浏览器表单往返。
208
+ 这些继续只在 `settings.yaml` 里配。
209
+
124
210
  ## 数据文件
125
211
 
126
212
  都在 `$DSH_HOME/user-management/`(默认 `~/.dsh/user-management/`,0600,原子写):
@@ -151,7 +237,7 @@ node scripts/import-from-dsh-passwords.mjs --from E:\path\to\dsh-passwords --ove
151
237
  ## 测试
152
238
 
153
239
  ```bash
154
- npm test # 93 项:策略纯函数、store、API 层、以及真实网关的端到端用例
240
+ npm test # 138 项:策略纯函数、store、API 层、客户端契约、以及真实网关的端到端用例
155
241
  npm run check # 全量语法检查
156
242
  ```
157
243