dsh-conversation-recycle-bin 0.2.2 → 0.3.1
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/CHANGELOG.md +36 -0
- package/README.md +89 -2
- package/client.js +444 -32
- package/config.schema.json +13 -0
- package/cordis.patch.yml +20 -0
- package/lib/core/config.js +40 -0
- package/lib/core/errors.js +6 -0
- package/lib/host/api.js +24 -3
- package/lib/host/feedback.js +434 -0
- package/lib/version.js +1 -1
- package/package.json +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,42 @@
|
|
|
3
3
|
All notable changes to `dsh-conversation-recycle-bin` are documented here.
|
|
4
4
|
The format follows Keep a Changelog; the project uses Semantic Versioning.
|
|
5
5
|
|
|
6
|
+
## [0.3.1] — 2026-10-18
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **表单改为让用户填写自己的邮箱**:反馈对话框里原来的「联系方式」换成「你的邮箱(选填)」`type="email"` 输入框,客户端与 Host 双重校验(必须是 `a@b.cc` 形状、≤200 字)。格式不合法直接以 `400 E_FEEDBACK_INVALID` + `details.field === 'email'` 拒绝,而不是默默发一封无法回复的邮件。
|
|
11
|
+
- 新增配置项 **`feedback.visitorAsFrom`**(默认 `false`):访客填写的邮箱默认写入 `Reply-To`,并在正文的 `from / 发件人` 行展示;设为 `true` 时同时作为 `From`——只有允许任意发件人的中继才行,QQ/163/Gmail 会拒收(要求 From 与登录账号一致)。`/state.feedback.visitorAsFrom` 会同步给前端,用于切换输入框下方的提示文案。
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- 邮件正文上下文字段由 `contact:` 改为 `from / 发件人:` + `reply-to:` 两行;HTTP 通道的 `reply_to` 取自访客邮箱。
|
|
16
|
+
- `test/feedback.test.mjs` 16 → 17 个用例(新增 `resolveSender` 发件人策略单测、非法邮箱拒绝);`test/client-module.test.mjs` 20 → 19(删除一条逐字节重复的用例外壳,行为覆盖不变),全量仍为 122。`README.md` §9.2 增加 `visitorAsFrom` 行与「发件人不是访客邮箱」排查项;`config.schema.json` / `cordis.patch.yml` 同步新键。
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **删除当前会话后的结果对话框会被兜底刷新抹掉**:Host 无法原地关闭该会话时会 arm 一次兜底刷新,原来是固定 900ms 后 `location.reload()`,弹窗里的「部分失败明细」根本来不及看。现在结果对话框会等用户关闭(按钮 / Escape / 点遮罩)后再刷新,5s 只是对话框被晾着的安全网;关闭时还会 `clearTimeout` 掉安全网计时器,避免关掉之后又被刷一次。对应文案 `dialog.reloadHint` 由「会话列表将自动刷新」改为「关闭本对话框后会刷新会话列表」。
|
|
21
|
+
- **反馈表单的邮箱校验顺序与 Host 相反**:客户端先判超长再判格式,300 字的地址会提示「不是邮箱」,而 Host 对同一次提交回的是「太长」。现在两边统一为「先形状、后长度」(对齐 `lib/host/feedback.js:241-250`)。
|
|
22
|
+
- **「记忆不可用」的原因混进英文硬编码**:无可用存储时 `not detected` 直接拼进中文文案。改用词条 `state.notDetected`(中英各一条)。
|
|
23
|
+
- **反馈表单的校验提示只在「输入过」时出现**:只用 Tab 扫过表单、一个字符没改的用户会看到一个禁用的提交按钮却看不到原因。现在「改过」或「离开过任一字段」(`dirty || touched`)即显示提示。
|
|
24
|
+
- **`noValidate` 挂在 `div` 上**:这是 form 的属性,放在对话框的 `div` 上无效,属误导性死属性,已删除(本组件不含 `<form>`,本身也不会触发原生校验)。
|
|
25
|
+
- **`test/client-module.test.mjs` 里有一条逐字节重复的用例**:同一段 `test('client: the active locale …')` 出现两次(HEAD 即如此),实际独立用例 19 条而计数写 20。删掉重复的一份,覆盖不变。
|
|
26
|
+
|
|
27
|
+
## [0.3.0] — 2026-10-18
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- **设置页「反馈」横幅(`feedback` 配置段 + `POST /conversation-recycle-bin/api/feedback`)**:设置页最上方新增一条「反馈」横幅(主色按钮,位于标题之上;`dc-feedback-bar`),表单含类型(问题 / 建议 / 其他)、标题、正文、可选联系方式,并自动附带插件版本、平台与语言(不含任何对话内容)。提交时按钮进入「正在发送…」禁用态,成功后弹 toast 并关闭对话框,失败按错误码给出可读原因。Host 侧 `lib/host/feedback.js` 负责校验、渲染与投递:标题 4–120 字、正文 10–4000 字、联系方式 ≤200 字(Host 复核,客户端只是提前禁用按钮);每条反馈每小时默认最多 5 条(`feedback.rateLimitPerHour`),全部从操作者自己的邮箱发出。
|
|
32
|
+
- **两种投递通道,均为环境变量注入凭证**:`http`(`DSH_CRB_MAIL_API_URL` + `DSH_CRB_MAIL_API_TOKEN`,POST JSON)与 `smtp`(`DSH_CRB_SMTP_URL`,按需 `import('nodemailer')`)。`nodemailer` 只声明为 `optionalDependencies`(`^10.1.2`),装不上不影响插件其余功能,缺失时明确返回 `reason: nodemailer-missing`。收件人/发件人由配置项 `feedback.to` / `feedback.from` 给出,**环境变量 `DSH_CRB_FEEDBACK_TO` / `DSH_CRB_FEEDBACK_FROM` 优先于它们**(同一份包被多人/多机使用时不必改配置文件);`feedback.to` 默认即维护者收件箱,以 `b64:<base64>` 形式随包分发(避免公开 tarball 中出现可被正则抓取的 `name@domain`;属混淆而非加密,解码器在 `lib/host/feedback.js`,明文地址同样可用),`feedback.from` 默认留空并复用 SMTP 连接串里的用户名(服务商要求 From 与登录账号一致)。**任何凭证都不进配置文件、不进代码库**。
|
|
33
|
+
- **反馈通道与删除开关解耦**:`enabled:false` 只关闭删除;`/state.feedback` 单独报告 `enabled`、`transport`、`toConfigured`。通道未配置时设置页不渲染按钮,直接请求也返回 `503 E_FEEDBACK_DISABLED`(带 `reason: not-configured | disabled | off`),而不是在点击后才失败。
|
|
34
|
+
- 新增错误码 `E_FEEDBACK_DISABLED`(503) / `E_FEEDBACK_INVALID`(400) / `E_FEEDBACK_RATE_LIMITED`(429) / `E_FEEDBACK_TRANSPORT`(502);投递失败只记错误码与 HTTP 状态,**正文、标题、联系方式不进 Host 日志**。
|
|
35
|
+
- **收件地址以 `b64:<base64>` 打包**:默认收件箱不再以明文 `name@domain` 出现在 `cordis.patch.yml` / `config.schema.json` / README / CHANGELOG 中,避免公开 tarball 被正则抓取。这是**混淆而非加密**——解码器 `decodeAddress()` 就在 `lib/host/feedback.js` 里,拿到包的人总能还原;明文地址与环境变量照旧可用。
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- `npm test` 增加 `test/feedback.test.mjs`(16 个用例:配置归一化、通道判定、校验/渲染、HTTP 通路、失败状态、限流窗口、未配置、SMTP 缺依赖、两条路由用例),用例总数 104 → 122(按 `^test(` 逐文件实测校正;本行原写 106 → 121 为陈旧错值);`npm run check` 增加 `lib/host/feedback.js` 语法检查。
|
|
40
|
+
- `cordis.patch.yml` / `config.schema.json` 增加 `feedback` 默认段(`to` 默认为维护者公开反馈邮箱、`from` 留空、`transport: auto`、`rateLimitPerHour: 5`)。
|
|
41
|
+
|
|
6
42
|
## [0.2.2] — 2026-10-18
|
|
7
43
|
|
|
8
44
|
对 0.2.1 的双环境(桌面/网页)全面审计修复轮。问题编号对应 `AUDIT-2026-10-17-desktop-web.md`;每个问题在该文档中都有平台、复现条件、原因与修复方向的完整记录。
|
package/README.md
CHANGED
|
@@ -188,8 +188,9 @@ dsh-conversation-recycle-bin/
|
|
|
188
188
|
│ ├─ catalog.js # archive/detach/pin 与日志移动
|
|
189
189
|
│ ├─ lifecycle.js # live 会话停机:cancel + archive + drain
|
|
190
190
|
│ ├─ memory.js # 适配器装配
|
|
191
|
+
│ ├─ feedback.js # 反馈校验/渲染/投递(http 或 smtp,凭证只读环境变量)
|
|
191
192
|
│ └─ api.js # 同源 HTTP API、确认 token、限流
|
|
192
|
-
└─ test/ #
|
|
193
|
+
└─ test/ # 122 个用例,覆盖用户要求的 5 类场景 + live 删除 + 记忆锁 + 原地刷新 + 部分失败/跨进程/未完成条目 + 反馈通道 + Client 半渲染冒烟
|
|
193
194
|
```
|
|
194
195
|
|
|
195
196
|
---
|
|
@@ -198,7 +199,7 @@ dsh-conversation-recycle-bin/
|
|
|
198
199
|
|
|
199
200
|
```powershell
|
|
200
201
|
cd D:\Users\34332\AI\dsh-conversation-recycle-bin
|
|
201
|
-
npm test # = manifest && client-module && core && cascade && memory && trash-audit && api(
|
|
202
|
+
npm test # = manifest && client-module && core && cascade && memory && trash-audit && feedback && api(122 个用例)
|
|
202
203
|
npm run check # 语法 + manifest
|
|
203
204
|
```
|
|
204
205
|
|
|
@@ -226,3 +227,89 @@ npm run check # 语法 + manifest
|
|
|
226
227
|
5. UI 依赖当前版本的 slot 约定;宿主升级若改 slot props,入口会失效但不会破坏宿主。0.1.4 的原地刷新还会调用 `uiWorkspace.clearArchivedCurrent/clearMain`(宿主 `.d.ts` 中标为 private);两者都不存在时会退回整页刷新,不会留下过期视图,但属于对私有方法的依赖,宿主升级需复核。
|
|
227
228
|
6. 回收站 manifest 的并发写由 `<dataDir>/mutation.lock` 跨进程锁保护(抢不到锁返回 `E_LOCK_BUSY`);但**会话日志内容**的跨进程写保护仍是 best-effort:`foreignWriterGraceMs` 只能发现"窗口内仍在增长"的日志,另一个 Host 的空闲 live 会话(两次写入之间)无法被本进程识别。桌面端与 `dsh web` 同时运行时,仍应先关闭另一端的会话再彻底删除。
|
|
228
229
|
7. 未完成条目(failed/pending/restoring/purging)若仍持有已移动的会话日志或记忆文件,出于数据安全不允许 `discard`(会报 `E_TRASH_CONFLICT`);设置页对这类条目提供「恢复」按钮,可直接恢复回会话列表,不再出现"失败了却没有出口"的死条目。日志与记忆都已不在磁盘上的条目才可以 discard。
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 9. 反馈通道(设置页「反馈」按钮)
|
|
234
|
+
|
|
235
|
+
设置页**最上方**新增一条「反馈」横幅(主色按钮,位于标题之上):把问题或建议写成一条消息,由**宿主进程**通过邮件发到维护者邮箱。凭证只在宿主侧读取,页面拿不到、也发不出任何邮件。
|
|
236
|
+
|
|
237
|
+
### 9.1 为什么是「页面 → 宿主路由 → 邮件」三段
|
|
238
|
+
|
|
239
|
+
浏览器既不能开 SMTP 连接,也不该持有邮件服务凭证。因此 `client.js` 只负责表单与校验提示,`POST /conversation-recycle-bin/api/feedback` 由 `lib/host/feedback.js` 校验、渲染并投递。表单里的「类型 / 标题 / 正文 / 你的邮箱」之外,只额外附带**对话框里已展示给用户**的上下文(插件版本、平台、语言);对话内容、会话 id、工作区路径都不会进入邮件。
|
|
240
|
+
|
|
241
|
+
### 9.2 配置项(`feedback` 段,非机密)
|
|
242
|
+
|
|
243
|
+
| 键 | 默认 | 说明 |
|
|
244
|
+
|---|---|---|
|
|
245
|
+
| `feedback.enabled` | `true` | 是否允许该通道。通道本身没配好时按钮依旧不显示 |
|
|
246
|
+
| `feedback.to` | `b64:…`(维护者收件箱) | 收件人。默认值以 `b64:<base64>` 形式携带,公开 tarball 里没有可直接抓取的 `name@domain` 串(这是**混淆,不是加密**:解码器就在 `lib/host/feedback.js`,拿到包的人总能解出来)。也接受明文地址;`DSH_CRB_FEEDBACK_TO` 优先于它 |
|
|
247
|
+
| `feedback.from` | `''` | 发件人。留空时 SMTP 通道复用连接串里的用户名(服务商通常要求 From 与登录账号一致);`DSH_CRB_FEEDBACK_FROM` 优先于它 |
|
|
248
|
+
| `feedback.transport` | `auto` | `auto` = 有 `DSH_CRB_MAIL_API_URL` 走 http,否则有 `DSH_CRB_SMTP_URL` 走 smtp;`http`/`smtp` 强制单通道(配不全就报未配置,不会静默换道);`off` 关闭 |
|
|
249
|
+
| `feedback.rateLimitPerHour` | `5` | 每宿主每小时发送上限(1–100)。邮件从操作者自己的账号发出,默认值刻意压低 |
|
|
250
|
+
| `feedback.visitorAsFrom` | `false` | 访客在表单里填的**自己的邮箱**默认只作为 `Reply-To`(并在正文里列出),`From` 仍是发信账号。设为 `true` 时把访客邮箱放进 `From`——只有允许任意发件人的中继才行,QQ/163/Gmail 会拒收(它们要求 From 与登录账号一致) |
|
|
251
|
+
|
|
252
|
+
### 9.3 环境变量(优先级高于配置项;凭证只走这里,不进配置文件)
|
|
253
|
+
|
|
254
|
+
> 冲突时**环境变量赢**:`DSH_CRB_FEEDBACK_TO` 覆盖 `feedback.to`,`DSH_CRB_FEEDBACK_FROM` 覆盖 `feedback.from`。这样多台机器/多人共用一个 package 时,各自改环境变量即可,不必改配置。
|
|
255
|
+
|
|
256
|
+
| 变量 | 用途 |
|
|
257
|
+
|---|---|
|
|
258
|
+
| `DSH_CRB_FEEDBACK_TO` | 收件人(优先于 `feedback.to`) |
|
|
259
|
+
| `DSH_CRB_FEEDBACK_FROM` | 发件人(优先于 `feedback.from`) |
|
|
260
|
+
| `DSH_CRB_MAIL_API_URL` | 邮件 HTTP API 地址(Resend 兼容形状,也可指向自建转发网关) |
|
|
261
|
+
| `DSH_CRB_MAIL_API_TOKEN` | 该 API 的 Bearer token |
|
|
262
|
+
| `DSH_CRB_MAIL_API_AUTH_HEADER` | 可选,鉴权头名,默认 `authorization`,值为 `Bearer <token>` |
|
|
263
|
+
| `DSH_CRB_SMTP_URL` | SMTP 连接串,如 `smtps://user:pass@smtp.example.com:465` |
|
|
264
|
+
|
|
265
|
+
HTTP 通道发出的 JSON 固定为 `{from, to: [收件人], subject, text, reply_to}`,`text` 是纯文本(不生成 HTML);`reply_to` 取自表单里访客填写的邮箱(留空则不填)。SMTP 通道按需 `import('nodemailer')`:它已声明为 `optionalDependencies`(`^10.1.2`),正常安装会随包就位;以 `link:` 方式开发安装时插件从自身真实目录解析,需在**本仓库**跑一次 `npm install`。仍未安装时返回 `E_FEEDBACK_TRANSPORT` + `reason: nodemailer-missing`。
|
|
266
|
+
|
|
267
|
+
#### 9.3.1 QQ 邮箱开通示例
|
|
268
|
+
|
|
269
|
+
QQ 邮箱不能直接用登录密码发信,要先取**授权码**(设置 → 账号 → POP3/SMTP 服务 → 开启 → 生成授权码),再把两件事写进环境变量(`@` 在连接串里必须写成 `%40`;`<你的QQ号>` 换成实际号码):
|
|
270
|
+
|
|
271
|
+
```powershell
|
|
272
|
+
setx DSH_CRB_SMTP_URL 'smtps://<你的QQ号>%40qq.com:你的授权码@smtp.qq.com:465'
|
|
273
|
+
# 可选:换收件人/发件人(不设就用包内默认的维护者收件箱)
|
|
274
|
+
setx DSH_CRB_FEEDBACK_TO 'your-feedback@example.com'
|
|
275
|
+
# 设置后重启 DSH 桌面端(setx 只对新进程生效)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
需要自己生成一个 `b64:` 地址(放进 profile patch 而不是环境变量时):
|
|
279
|
+
|
|
280
|
+
```powershell
|
|
281
|
+
node -e "console.log('b64:' + Buffer.from('your-feedback@example.com','utf8').toString('base64'))"
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
### 9.4 本地验证
|
|
286
|
+
|
|
287
|
+
```powershell
|
|
288
|
+
cd D:\Users\34332\AI\dsh-conversation-recycle-bin
|
|
289
|
+
npm run check # 语法 + manifest(含 feedback.js)
|
|
290
|
+
node test/feedback.test.mjs # 17 个反馈用例
|
|
291
|
+
npm test # 全量 122 个用例
|
|
292
|
+
|
|
293
|
+
# 端到端:用本机收件桩观察真实投递路径,不外发邮件
|
|
294
|
+
$env:DSH_CRB_MAIL_API_URL = 'http://127.0.0.1:9099/emails'
|
|
295
|
+
$env:DSH_CRB_MAIL_API_TOKEN = 'dev-token'
|
|
296
|
+
$env:DSH_CRB_FEEDBACK_TO = 'feedback@example.test'
|
|
297
|
+
# 另一个终端起桩:
|
|
298
|
+
# node -e "require('http').createServer((q,s)=>{let b='';q.on('data',c=>b+=c);q.on('end',()=>{console.log(q.method,q.url,b);s.end('{}')})}).listen(9099)"
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
起桩后打开设置页点「反馈」,填表发送:桩终端应打印 `POST /emails {...}`,页面出现「反馈已发送,谢谢!」。测 SMTP 通路时改设 `DSH_CRB_SMTP_URL`(本仓库已按 `optionalDependencies` 声明 `nodemailer`,先在本仓库 `npm install` 一次;未安装时返回 `E_FEEDBACK_TRANSPORT` + `reason: nodemailer-missing`,这是预期行为)。
|
|
302
|
+
|
|
303
|
+
### 9.5 失败排查
|
|
304
|
+
|
|
305
|
+
| 现象 | 原因 | 处理 |
|
|
306
|
+
|---|---|---|
|
|
307
|
+
| 设置页没有「反馈」按钮 | 通道未配置(`/state.feedback.enabled === false`) | 设好 `DSH_CRB_MAIL_API_URL` + token,或 `DSH_CRB_SMTP_URL`;确认 `feedback.transport` 不是 `off`、`enabled` 不是 `false` |
|
|
308
|
+
| 点击后 503 `E_FEEDBACK_DISABLED` | 同上一行(按钮来自旧页面渲染) | 刷新页面;看 `/state.feedback.reason`:`not-configured` / `disabled` / `off` |
|
|
309
|
+
| 400 `E_FEEDBACK_INVALID` | 标题 4–120 字、正文 10–4000 字、邮箱 ≤200 字且必须形如 `a@b.cc` | `details.field` 指明字段,`details.min`/`details.max` 给出边界 |
|
|
310
|
+
| 429 `E_FEEDBACK_RATE_LIMITED` | 超过 `feedback.rateLimitPerHour` | 等 `details.retryAfterMs` 后重试,或调高上限 |
|
|
311
|
+
| 502 `E_FEEDBACK_TRANSPORT` | 邮件服务返回非 2xx / 网络失败 / 未装 nodemailer / 暴露不出 `createTransport` | 看宿主日志 `[conversation-recycle-bin] feedback delivery failed (<transport>): <code> <message>`(只含错误码与通道,不含正文);核对 URL、token、SMTP 授权码 |
|
|
312
|
+
| 邮件里「发件人」不是访客的邮箱 | 默认只把访客邮箱放进 `Reply-To`(`visitorAsFrom: false`),发信人仍是发信账号 | QQ/163/Gmail 必须如此(From 必须等于登录账号);换用允许任意发件人的中继后再把 `feedback.visitorAsFrom` 设为 `true` |
|
|
313
|
+
| 接口 200 但收件箱没有 | 服务端已受理,但被对方退信或进了垃圾箱 | 查发件账号退信;`feedback.from` 用与 SMTP 账号一致的地址,并检查收件人域名 |
|
|
314
|
+
|
|
315
|
+
> 隐私边界:邮件正文 = 用户输入 + 对话框里已展示的上下文;宿主日志只记错误码、通道与耗时。删除开关(`enabled`)与反馈通道互相独立——关掉删除不影响反馈,反之亦然。
|