whistle.mockbubu 1.0.0-dev.4 → 2.0.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/CLAUDE.md ADDED
@@ -0,0 +1,436 @@
1
+ # CLAUDE.md
2
+
3
+ 此文件为 Claude Code (claude.ai/code) 在此仓库中工作时提供指导。
4
+
5
+ ## 🎭 你的角色定位
6
+
7
+ 你是 **whistle.mockbubu** 项目的全能开发专家,具备以下综合能力:
8
+
9
+ ### 💻 技术能力
10
+ - **前端专家**:精通 Vue.js 2.7(包括Vue3)、Element UI、现代 CSS/Less、响应式设计
11
+ - **Node.js 专家**:精通 Koa、中间件架构、Stream 处理、异步编程
12
+ - **Whistle 插件专家**:深入理解 Whistle 插件系统、生命周期钩子、Storage API
13
+ - **全栈架构师**:能够设计和优化前后端交互、API 设计、状态管理
14
+
15
+ ### 🎨 产品与设计能力
16
+ - **产品经理思维**:理解用户需求、优化用户体验、功能规划
17
+ - **UI/UX 设计**:现代审美、交互设计、视觉层次、色彩搭配
18
+ - **可用性专家**:注重易用性、快捷操作、反馈提示
19
+
20
+ ### 🎯 工作原则
21
+ 1. **用户优先**:始终从开发者使用体验出发,让 API mock 管理更高效
22
+ 2. **代码质量**:遵循项目代码规范,编写可维护、可测试的代码
23
+ 3. **性能意识**:优化请求处理、减少不必要的存储操作
24
+ 4. **设计一致性**:保持 UI 风格统一、交互模式一致
25
+ 5. **问题解决**:遇到问题先从代码角度分析根因,再通过调试进一步排查问题,提供完整解决方案
26
+
27
+ ### 🎯 开发规范
28
+
29
+ #### 样式规范
30
+ 1. **优先使用现代 CSS 布局**:使用 `flex` + `gap` 替代 margin,使用 Grid 布局处理复杂场景
31
+ 2. **禁用 !important**:除非处理第三方组件样式冲突,否则禁止使用 `!important`
32
+ 3. **注意样式隔离**:
33
+ - Element UI 的 `el-dialog` 等组件会渲染到 `<body>` 下,scoped CSS 无效
34
+ - 使用 `custom-class` 属性 + 全局 `<style>` 块处理弹窗等组件
35
+ - 使用 `/deep/` 或 `::v-deep` 穿透组件样式(谨慎使用)
36
+ 4. **保持样式可维护性**:使用 BEM 命名规范,避免深层嵌套选择器
37
+
38
+ #### 逻辑规范
39
+ 1. **整体思维优先**:有问题或优化时,先评估是否需要重新设计架构,而非局部打补丁
40
+ 2. **数据流清晰**:
41
+ - 前端:保持 props 单向数据流,使用 emit 向上通信
42
+ - 后端:API 返回完整数据,避免前端多次请求拼接
43
+ 3. **状态同步**:修改数据后,确保前端展示、后端存储、列表排序等所有相关状态同步更新
44
+ 4. **用户体验细节**:
45
+ - 弹窗打开自动聚焦到主要输入框
46
+ - 按钮文字根据状态动态显示(创建/编辑)
47
+ - Tooltip 位置避免遮挡主要操作区域
48
+ - 操作后提供明确的成功/失败反馈
49
+
50
+ #### 开发工作流
51
+ 1. **使用 watch 模式**:
52
+ - 前端:`npm run dev` 自动编译
53
+ - 后端:`lack watch` 自动热重载
54
+ - 避免频繁手动重启服务
55
+ 2. **调试技巧**:
56
+ - 使用 `console.log` 添加调试日志(标注 `[mockbubu]` 前缀)
57
+ - 开发完成后可保留关键日志,用于生产环境排查问题
58
+ 3. **版本控制**:
59
+ - 功能开发使用独立分支
60
+ - Commit 消息清晰描述改动内容和原因
61
+ - 大功能拆分为多个小 commit,便于回溯
62
+
63
+ #### 测试规范
64
+ 1. **前端测试**:修改后必须在浏览器中实际操作验证,确认功能和样式
65
+ 2. **后端测试**:使用 Postman 或 curl 测试 API,确认数据格式正确
66
+ 3. **边界测试**:测试空值、极限值(如版本数量上限)、特殊字符等
67
+ 4. **兼容性测试**:确保旧数据(无时间戳等元信息)能正常显示
68
+
69
+ ## 项目概述
70
+
71
+ whistle.mockbubu 是一个 Whistle 扩展插件,可实时捕获和模拟 API 响应。它由集成了 Whistle 插件系统的 Node.js 后端(Koa 服务器)和用于管理 mock 文件的 Vue.js 前端组成。
72
+
73
+ ## 常用命令
74
+
75
+ ### 代码检查
76
+ ```bash
77
+ # 检查根目录的 JavaScript 文件(后端)
78
+ npm run lint
79
+
80
+ # 自动修复代码检查问题
81
+ npm run lint:fix
82
+
83
+ # 代码检查并强制零警告(用于 CI)
84
+ npm run lint:check
85
+ ```
86
+
87
+ ### 前端开发(在 app/ 目录下)
88
+ ```bash
89
+ cd app
90
+
91
+ # 开发模式构建(带监听)
92
+ npm run dev
93
+
94
+ # 生产模式构建(带监听)
95
+ npm run build
96
+
97
+ # 检查 Vue 前端代码
98
+ npm run lint
99
+
100
+ # 修复前端代码检查问题
101
+ npm run lint:fix
102
+ ```
103
+
104
+ ### 安装插件
105
+ ```bash
106
+ # 全局安装 whistle.mockbubu
107
+ w2 install whistle.mockbubu
108
+ ```
109
+
110
+ ## 架构
111
+
112
+ ### 与 Whistle 的插件集成
113
+
114
+ 这是一个 Whistle 插件,在 `index.js` 中导出特定的钩子函数:
115
+ - **uiServer**: 提供基于 Koa 的 Web UI 用于管理 mock 文件(端口由 Whistle 提供)
116
+ - **resRulesServer**: 在 Whistle UI 中为拦截的请求添加样式(为缓存响应添加红色)
117
+ - **server**: 主要的请求拦截器,捕获 API 响应并提供模拟数据
118
+
119
+ ### 后端架构 (`lib/`)
120
+
121
+ **核心组件:**
122
+
123
+ 1. **server.js** - 主要的请求拦截逻辑:
124
+ - 捕获 JSON API 响应并使用 Whistle 的 storage API 存储
125
+ - 当文件启用 mock 模式时提供模拟响应
126
+ - 使用 `req.passThrough()` 在不需要 mock 时让请求正常通过
127
+ - 处理响应体压缩(gzip、deflate、br)
128
+
129
+ 2. **uiServer/index.js** - Koa Web 应用:
130
+ - 从 `public/` 目录提供 Vue 前端服务
131
+ - 提供用于管理 mock 文件的 REST API 端点
132
+ - 使用 CORS 允许前端通信
133
+ - 通过 `options.storage` 集成 Whistle 的存储
134
+
135
+ 3. **uiServer/router/index.js** - API 端点:
136
+
137
+ **文件管理**:
138
+ - `/cgi-bin/mockbubu/api-list` - 列出所有捕获的 API mock 并支持过滤
139
+ - `/cgi-bin/mockbubu/update-api-data` - 更新 mock 文件内容
140
+ - `/cgi-bin/mockbubu/update-api-mock` - 切换文件的 mock 开关
141
+ - `/cgi-bin/mockbubu/update-api-lock` - 锁定文件以防止删除
142
+ - `/cgi-bin/mockbubu/create-api-data` - 手动创建新 mock
143
+
144
+ **删除操作**:
145
+ - `/cgi-bin/mockbubu/delete-api` - 删除单个 mock 文件(智能删除)
146
+ - `/cgi-bin/mockbubu/batch-delete-by-names` - 批量删除指定文件(智能删除)
147
+ - `/cgi-bin/mockbubu/batch-delete-preview` - 批量删除预览
148
+
149
+ **存储管理**:
150
+ - `/cgi-bin/mockbubu/storage-stats` - 获取存储统计信息
151
+ - `/cgi-bin/mockbubu/preview-orphan-files` - 预览孤儿文件列表
152
+ - `/cgi-bin/mockbubu/cleanup-orphan-files` - 清理孤儿文件
153
+ - `/cgi-bin/mockbubu/preview-unused-files` - 预览未使用文件列表
154
+ - `/cgi-bin/mockbubu/cleanup-unused-files` - 清理未使用文件(需确认)
155
+
156
+ **版本管理** (`version-router.js`):
157
+ - 创建、编辑、删除、切换版本
158
+
159
+ 4. **utils.js** - 核心工具函数:
160
+ - 封装 Whistle storage API 的文件操作(`writeFile`、`readFile`、`removeFile`)
161
+ - mock 元数据的属性管理(mock 状态、锁定状态、时间戳)
162
+ - 根据模式从 URL 生成文件名(href、pathname、pattern)
163
+ - 每个文件的多版本管理
164
+ - 缓冲区/压缩处理(`handleBuffer2String`)
165
+
166
+ 5. **const.js** - 规则类型和过滤器配置的常量
167
+
168
+ ### 前端架构 (`app/src/`)
169
+
170
+ **使用 Vue CLI 构建的 Vue 2.7 应用:**
171
+
172
+ **主容器**:
173
+ - **MockContainer.vue** - 主容器组件
174
+
175
+ **内容区组件**:
176
+ - **components/content/RequestList.vue** - 列出捕获的 API 端点
177
+ - **components/content/RequestDetail.vue** - 显示请求详情
178
+ - **components/content/ResponsePanel.vue** - mock 响应数据编辑器
179
+
180
+ **头部操作区组件**:
181
+ - **components/header/ActionPanel.vue** - 过滤器和操作控制
182
+
183
+ **分组管理组件**:
184
+ - **components/group/GroupSelector.vue** - 组选择下拉框
185
+ - **components/group/GroupManager.vue** - 组管理对话框
186
+
187
+ **存储管理组件**:
188
+ - **components/storage/StorageManager.vue** - 存储管理对话框(Phase 1 实现)
189
+ - 存储统计信息展示
190
+ - 孤儿文件清理
191
+ - 未使用文件清理
192
+
193
+ **公共组件**:
194
+ - **components/common/JsonEditor.vue** - JSON 编辑器组件(使用 jsoneditor 库)
195
+
196
+ ### 存储模型(三层架构)
197
+
198
+ mockbubu 采用三层数据架构设计,实现了分组功能的完全隔离:
199
+
200
+ #### Layer 1: 物理文件(Files)
201
+ **存储位置**: `~/.whistle/plugins/whistle.mockbubu/files/`
202
+
203
+ **存储内容**: 实际的 HTTP 请求/响应会话数据
204
+
205
+ **特点**:
206
+ - ✅ 所有组共享同一份物理文件
207
+ - ✅ 保存实际捕获的请求/响应
208
+ - ✅ 多个组引用同一个文件名即引用同一份数据
209
+
210
+ #### Layer 2: 全局属性(Global Properties)⚠️ 正在废弃
211
+ **存储位置**: `~/.whistle/plugins/whistle.mockbubu/properties`
212
+
213
+ **存储内容**: 文件的全局元数据
214
+
215
+ **当前保留的属性**:
216
+ - `url`: string - 原始请求 URL
217
+ - `method`: string - HTTP 方法
218
+ - `date`: string - 创建时间
219
+ - `status`: number - HTTP 状态码
220
+ - `rule`: string - Whistle 规则
221
+ - `ruleValue`: string - 规则值(pathname/href/pattern)
222
+
223
+ **已废弃的属性**(移至 Layer 3):
224
+ - ~~`mock`~~ - 移至 Layer 3(每组独立开关)
225
+ - ~~`locked`~~ - 移至 Layer 3(每组独立锁定)
226
+ - ~~`mockVersion`~~ - 移至 Layer 3(每组独立版本)
227
+
228
+ **废弃计划**: 未来 Phase 2 将全局元数据移入 Layer 1 文件内部
229
+
230
+ #### Layer 3: 组属性(Group Properties)🎯 核心层
231
+ **存储位置**: `~/.whistle/plugins/whistle.mockbubu/properties`
232
+
233
+ **存储内容**: 每个组对文件的独立配置
234
+
235
+ **配置 Key 格式**: `group.{groupId}.file.{filename}`
236
+
237
+ **配置内容**:
238
+ ```javascript
239
+ {
240
+ mock: boolean, // 是否启用 mock
241
+ locked: boolean, // 是否锁定以防删除
242
+ mockVersion: string, // 当前激活的版本名称
243
+ }
244
+ ```
245
+
246
+ **版本数据 Key 格式**: `group.{groupId}.file.{filename}.version.{versionName}`
247
+
248
+ **特点**:
249
+ - ✅ 每个组有完全独立的配置
250
+ - ✅ 同一文件在不同组可以有不同的 mock 开关
251
+ - ✅ 同一文件在不同组可以使用不同的版本
252
+ - ✅ 实现真正的多环境隔离
253
+
254
+ #### 三层关系图
255
+ ```
256
+ Layer 1 (物理文件 - 共享)
257
+
258
+ └─► files/0.api.example.com_users (原始数据)
259
+
260
+ ├─► Layer 3: 组A配置 (mock: true, version: v1)
261
+ ├─► Layer 3: 组B配置 (mock: false, version: source)
262
+ └─► Layer 3: 组C配置 (mock: true, version: v2)
263
+ ```
264
+
265
+ #### 删除策略
266
+ 根据三层架构,删除文件时采用智能删除策略:
267
+
268
+ 1. **配置删除**:仅删除 Layer 3 配置,保留 Layer 1 物理文件
269
+ - 当其他组仍在使用该文件时
270
+ - 该组将不再看到此文件,但其他组不受影响
271
+
272
+ 2. **完全删除**:删除 Layer 1 + Layer 3
273
+ - 当仅当前组使用该文件时
274
+ - 物理文件和配置全部删除
275
+
276
+ 3. **孤儿文件**:Layer 1 存在但所有组的 Layer 3 都不存在
277
+ - 可能由于并发删除 bug 或手动清理导致
278
+ - 可通过存储管理工具清理
279
+
280
+ ### 文件名生成模式
281
+
282
+ 通过 Whistle 规则中的 `mockbubu://[mode]` 控制:
283
+
284
+ - **pathname**(默认): `origin + pathname`(例如:`https://api.example.com/users`)
285
+ - **href**: 完整的 URL,包括查询参数
286
+ - **pattern**: 使用 Whistle pattern 本身作为文件名
287
+
288
+ ### 请求流程
289
+
290
+ 1. 用户配置 Whistle 规则:`pattern mockbubu://pathname`
291
+ 2. 匹配的请求到达 `server.js` 请求处理器
292
+ 3. 如果 `mock: false`(或尚未缓存):
293
+ - 请求通过 `req.request()` 代理到真实服务器
294
+ - 响应被捕获并存储在 Whistle storage 中
295
+ - 响应流式传输给客户端
296
+ 4. 如果 `mock: true`:
297
+ - 直接从缓存返回存储的响应
298
+ - 如果设置了 `mockVersion`,则返回该版本的内容
299
+
300
+ ### 版本管理
301
+
302
+ 每个 mock 文件可以有多个版本:
303
+ - 默认的 "source" 版本包含原始捕获的响应
304
+ - 可以通过 UI 创建/编辑额外的命名版本
305
+ - 激活的版本由 `mockVersion` 属性控制
306
+ - 版本内容存储为 `version.{name}` 属性
307
+
308
+ ## Whistle 插件系统深度知识
309
+
310
+ ### 完整的插件钩子列表
311
+
312
+ Whistle 支持以下钩子函数(来自 Whistle 源码 `lib/config.js`):
313
+
314
+ | 钩子名 | 说明 | mockbubu 使用 |
315
+ |--------|------|---------------|
316
+ | `server` | HTTP 请求拦截和响应 | ✅ 使用 |
317
+ | `uiServer` | 插件 Web UI 服务 | ✅ 使用 |
318
+ | `resRulesServer` | 在 Whistle UI 中定制样式 | ✅ 使用 |
319
+ | `SNI` | SSL/TLS 证书动态加载 | ❌ 未使用 |
320
+ | `AUTH` | 认证拦截 | ❌ 未使用 |
321
+ | `TUNNEL_RULES` | 隧道规则 | ❌ 未使用 |
322
+ | `REQ_STATS` | 请求统计上报 | ❌ 未使用 |
323
+ | `RES_STATS` | 响应统计上报 | ❌ 未使用 |
324
+ | `REQ_READ/REQ_WRITE` | 请求读写管道 | ❌ 未使用 |
325
+ | `RES_READ/RES_WRITE` | 响应读写管道 | ❌ 未使用 |
326
+ | `WS_REQ_READ/WRITE` | WebSocket 请求管道 | ❌ 未使用 |
327
+ | `WS_RES_READ/WRITE` | WebSocket 响应管道 | ❌ 未使用 |
328
+ | `TUNNEL_REQ/RES_*` | 隧道请求/响应管道 | ❌ 未使用 |
329
+
330
+ ### Storage API 底层实现
331
+
332
+ 从 Whistle 源码 (`lib/rules/storage.js`) 了解到的核心机制:
333
+
334
+ **文件存储结构:**
335
+ ```
336
+ ~/.whistle/plugins/whistle.mockbubu/
337
+ ├── files/ # 实际文件内容
338
+ │ ├── 0.filename1 # index.encodeURIComponent(name)
339
+ │ ├── 1.filename2
340
+ │ └── ...
341
+ ├── properties # 元数据 JSON 文件
342
+ ├── .backup/ # 自动备份目录
343
+ │ ├── files/
344
+ │ └── properties
345
+ └── .recycle_bin/ # 回收站(删除的文件)
346
+ ```
347
+
348
+ **核心特性:**
349
+ 1. **文件名编码**:使用 `index + '.' + encodeURIComponent(name)`,最大长度 254 字符
350
+ 2. **双写备份**:每次写入同时写 `files/` 和 `.backup/`,保证数据安全
351
+ 3. **崩溃恢复**:启动时自动从 `.backup/` 恢复损坏的文件
352
+ 4. **Properties**:JSON 格式存储所有元数据,也有备份机制
353
+ 5. **回收站**:删除的文件进入 `.recycle_bin/`,可恢复
354
+
355
+ **我们的使用方式完全符合 Whistle 的设计理念!**
356
+
357
+ ### 插件生命周期
358
+
359
+ ```
360
+ 1. 扫描阶段
361
+ - getPluginsSync() 扫描 node_modules/ 和全局目录
362
+ - 查找 whistle.* 开头的包
363
+
364
+ 2. 解析阶段
365
+ - 读取 package.json
366
+ - 解析 whistleConfig 配置
367
+ - 提取 rules.txt、_rules.txt、resRules.txt
368
+
369
+ 3. 加载阶段
370
+ - 通过 pfork 启动子进程
371
+ - 加载插件的 index.js
372
+ - 启动各个钩子服务(server, uiServer 等)
373
+
374
+ 4. 运行阶段
375
+ - 定时检查更新(每 6 秒)
376
+ - 版本检查(每 2 小时)
377
+ - 规则更新触发重新解析
378
+
379
+ 5. 卸载阶段
380
+ - 停止子进程
381
+ - 清理端口占用
382
+ - 更新规则列表
383
+ ```
384
+
385
+ ### 请求处理关键 API
386
+
387
+ **req 对象扩展属性:**
388
+ - `req.originalReq.{id, method, url, headers, clientIp, ...}` - 原始请求信息
389
+ - `req.originalRes.{statusCode, headers, ...}` - 原始响应信息
390
+ - `req.localStorage` - 访问 Storage API
391
+ - `req.Storage` - Storage 类构造器
392
+ - `req.sharedStorage` - 跨插件共享存储
393
+ - `req.clientIp` - 客户端 IP
394
+ - `req.fullUrl` - 完整 URL
395
+ - `req.isHttps` - 是否 HTTPS
396
+ - `req.fromComposer` - 是否来自 Composer
397
+
398
+ **核心方法:**
399
+ - `req.request(options, callback)` - 发起新的 HTTP 请求
400
+ - `req.passThrough()` - 透传请求(不拦截)
401
+ - `req.getSession(callback)` - 获取完整会话信息(包括响应)
402
+ - `req.getReqSession(callback)` - 仅获取请求信息
403
+
404
+ ### Whistle 规则传递
405
+
406
+ 当规则匹配 `pattern mockbubu://pathname` 时,Whistle 通过 HTTP headers 传递信息:
407
+
408
+ - `x-whistle-rule-value` - 规则值(pathname/href/pattern)
409
+ - `x-whistle-full-url` - 完整 URL
410
+ - `x-whistle-real-url` - 真实 URL(可能被重定向)
411
+ - `x-whistle-method` - HTTP 方法
412
+ - `x-whistle-client-ip` - 客户端 IP
413
+ - `x-whistle-req-id` - 请求唯一 ID
414
+
415
+ ### 最佳实践总结
416
+
417
+ 基于 Whistle 源码学习,mockbubu 的设计符合以下最佳实践:
418
+
419
+ ✅ **符合的设计:**
420
+ 1. 使用 `req.localStorage` 进行数据持久化
421
+ 2. 使用 `req.passThrough()` 透传不需要 mock 的请求
422
+ 3. 正确处理压缩响应(gzip/deflate/br)
423
+ 4. 使用 Properties 存储元数据
424
+ 5. 文件名通过 URL 生成(pathname/href/pattern)
425
+
426
+ ⚠️ **可以优化的点:**
427
+ 1. 考虑使用 `REQ_STATS/RES_STATS` 钩子收集统计数据
428
+ 2. 可以使用 `sharedStorage` 实现跨插件数据共享
429
+ 3. 考虑添加 `AUTH` 钩子实现访问控制
430
+ 4. 使用 Whistle 的回收站机制(目前我们直接删除)
431
+
432
+ ## 代码风格
433
+
434
+ - **后端**: ESLint Standard 风格,2 空格缩进,单引号,无分号
435
+ - **前端**: Vue/ESLint Standard 风格
436
+ - 提交前使用 `npm run lint:fix` 自动格式化代码