whistle.mockbubu 2.0.0 → 2.1.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.
Files changed (56) hide show
  1. package/README.md +38 -0
  2. package/index.js +3 -3
  3. package/lib/config/const.js +138 -0
  4. package/lib/config/rule-collector.js +81 -0
  5. package/lib/constants.js +62 -0
  6. package/lib/core/memory-buffer/index.js +207 -0
  7. package/lib/core/memory-buffer/shared-instance.js +15 -0
  8. package/lib/core/plugin-mode-manager.js +74 -0
  9. package/lib/core/resRulesServer.js +14 -0
  10. package/lib/core/rulesServer.js +31 -0
  11. package/lib/core/server-entry/capture-handler.js +191 -0
  12. package/lib/core/server-entry/request-interceptor.js +82 -0
  13. package/lib/core/server-entry/response-handler.js +147 -0
  14. package/lib/core/server-entry/server.js +230 -0
  15. package/lib/storage/group-manager.js +627 -0
  16. package/lib/storage/storage-adapter.js +712 -0
  17. package/lib/storage/storage-v3.js +1418 -0
  18. package/lib/uiServer/index.js +61 -24
  19. package/lib/uiServer/router/export/import-export-router.js +459 -0
  20. package/lib/uiServer/router/files/api-list-router.js +150 -0
  21. package/lib/uiServer/router/files/batch-operations-router.js +185 -0
  22. package/lib/uiServer/router/files/file-config-router.js +118 -0
  23. package/lib/uiServer/router/files/file-crud-router.js +212 -0
  24. package/lib/uiServer/router/files/file-save-router.js +146 -0
  25. package/lib/uiServer/router/files/version-router.js +260 -0
  26. package/lib/uiServer/router/global/plugin-control.js +135 -0
  27. package/lib/uiServer/router/global/system-info-router.js +386 -0
  28. package/lib/uiServer/router/{group-router.js → groups/group-router.js} +21 -20
  29. package/lib/uiServer/router/index.js +38 -1521
  30. package/lib/uiServer/utils/router-helpers.js +100 -0
  31. package/lib/uiServer/utils/util.js +172 -0
  32. package/lib/uiServer/{validator.js → utils/validator.js} +11 -6
  33. package/lib/utils/archive-utils.js +788 -0
  34. package/lib/utils/error-handler.js +173 -0
  35. package/lib/utils/logger.js +79 -0
  36. package/lib/utils/path-utils.js +147 -0
  37. package/lib/utils/performance.js +265 -0
  38. package/lib/utils/utils.js +541 -0
  39. package/package.json +2 -2
  40. package/public/js/app.js +3732 -1929
  41. package/public/js/app.js.map +1 -1
  42. package/public/js/chunk-vendors.js +5098 -3965
  43. package/public/js/chunk-vendors.js.map +1 -1
  44. package/rules.txt +1 -1
  45. package/CHANGELOG_GROUP_FEATURE.md +0 -468
  46. package/CHANGELOG_P0_FIXES.md +0 -412
  47. package/CHANGELOG_P1_OPTIMIZATIONS.md +0 -292
  48. package/CLAUDE.md +0 -436
  49. package/GROUP_FEATURE_DESIGN.md +0 -520
  50. package/lib/const.js +0 -47
  51. package/lib/group-manager.js +0 -491
  52. package/lib/resRulesServer.js +0 -9
  53. package/lib/server.js +0 -249
  54. package/lib/uiServer/router/version-router.js +0 -205
  55. package/lib/uiServer/util.js +0 -153
  56. package/lib/utils.js +0 -409
package/lib/server.js DELETED
@@ -1,249 +0,0 @@
1
- const {
2
- setProperty,
3
- getFilename,
4
- getRule,
5
- isJsonReq,
6
- setApiListUpdated,
7
- handleBuffer2String,
8
- withTryCatch,
9
- } = require('./utils')
10
- const qs = require('qs')
11
- const GroupManager = require('./group-manager')
12
-
13
- module.exports = (server, { storage }) => {
14
- // 初始化组管理器
15
- const groupManager = new GroupManager(storage)
16
-
17
- // 首次启动时迁移现有数据到默认组
18
- groupManager.migrateExistingData()
19
-
20
- server.on('request', (req, res) => {
21
- const { originalReq, method } = req
22
- const { headers, ruleValue, url, pattern } = originalReq
23
- const filename = getFilename(originalReq)
24
- const rule = getRule(originalReq)
25
-
26
- // 非json请求直接透传
27
- if (!isJsonReq(headers)) {
28
- console.log(`[mockbubu] ⏭️ 非 JSON 请求已跳过 | File: ${filename}`)
29
- console.log(`[mockbubu] 📋 Headers: Accept=${headers.accept}, Sec-Fetch-Dest=${headers['sec-fetch-dest'] || headers['Sec-Fetch-Dest']}`)
30
- return req.passThrough()
31
- }
32
-
33
- // 获取请求信息 - 这些写入到全局 properties
34
- let str = ''
35
- req.on('data', (chunk) => {
36
- str += chunk
37
- })
38
- req.on('end', () => {
39
- setProperty(storage, filename, { payload: str })
40
- })
41
- setProperty(storage, filename, {
42
- query: qs.parse(new URL(url).searchParams.toString()),
43
- })
44
-
45
- try {
46
- // 获取当前激活的组ID
47
- const currentGroupId = groupManager.getCurrentGroupId()
48
- // 读取该组的配置
49
- const groupConfig = groupManager.getGroupFileConfig(currentGroupId, filename)
50
- const { mock, mockVersion } = groupConfig
51
-
52
- // 添加调试日志 - 使用一行显示关键信息
53
- console.log(`[mockbubu] 🔍 REQUEST | File: ${filename} | Group: ${currentGroupId} | Mock: ${mock} | Version: ${mockVersion || 'source'}`)
54
- console.log('[mockbubu] 📝 Full Config:', JSON.stringify(groupConfig, null, 2))
55
-
56
- // 从组目录读取缓存文件
57
- const filePath = `${currentGroupId}/${filename}`
58
- const sessionCache = storage.readFile(filePath)
59
- console.log(`[mockbubu] 💾 Cache Status: ${sessionCache ? 'EXISTS' : 'NO CACHE'} | Path: ${filePath}`)
60
-
61
- if (mock && sessionCache) {
62
- // Mock: true 且有缓存,返回模拟数据
63
- const resCache = JSON.parse(sessionCache)?.res
64
- const { statusCode, statusMessage, headers } = resCache
65
-
66
- headers['from-res-cache'] = 'true'
67
- delete headers['content-encoding']
68
- delete headers['content-length']
69
- res.writeHead(statusCode, statusMessage, headers)
70
-
71
- if (mockVersion) {
72
- // 从当前组配置读取版本内容
73
- console.log(`[mockbubu] ⚡ 使用版本数据 | Version: ${mockVersion}`)
74
- const mockVersionContent = groupManager.getGroupVersionContent(currentGroupId, filename, mockVersion)
75
- if (mockVersionContent) {
76
- console.log(`[mockbubu] ✅ 版本数据存在,返回版本: ${mockVersion}`)
77
- res.end(JSON.stringify(mockVersionContent))
78
- } else {
79
- // 版本不存在时降级到原始数据
80
- console.log(`[mockbubu] ⚠️ 版本 ${mockVersion} 不存在,降级到原始数据`)
81
- res.end(resCache.body)
82
- }
83
- } else {
84
- console.log('[mockbubu] ⚡ 使用原始数据 (无版本)')
85
- res.end(resCache.body)
86
- }
87
- } else if (mock && !sessionCache) {
88
- // Mock: true 但无缓存,需要先捕获数据
89
- console.log(`[mockbubu] ⚠️ Mock enabled but no cache for ${filename}, need to capture first`)
90
- // 不要 return,继续往下执行捕获逻辑
91
- // 无缓存,捕获最新的接口数据
92
- console.log(`[mockbubu] 🎣 开始捕获(无缓存): ${filename}`)
93
- const client = req.request((svrRes) => {
94
- const encoding = svrRes.headers['content-encoding']
95
- let body
96
-
97
- svrRes.on('data', (data) => {
98
- body = body ? Buffer.concat([body, data]) : data
99
- })
100
- svrRes.on('end', withTryCatch(async () => {
101
- if (!body) return
102
-
103
- const content = await handleBuffer2String({ body, encoding })
104
- // 获取完整的抓包数据,要等待响应完成
105
- req.getSession(async (session) => {
106
- // 如果设置了 enable://hide 会获取到空数据
107
- if (!session) {
108
- return
109
- }
110
-
111
- // 完全隔离架构:将元数据和配置写入当前组
112
- // 检查组配置是否真实存在(而不是默认返回值)
113
- const configKey = groupManager.getGroupConfigKey(currentGroupId, filename)
114
- const isFirstCapture = !storage.hasProperty(configKey)
115
-
116
- if (isFirstCapture) {
117
- // 首次捕获:创建完整配置(保持当前 mock 状态)
118
- groupManager.setGroupFileConfig(currentGroupId, filename, {
119
- method,
120
- rule,
121
- status: session.res.statusCode,
122
- pattern,
123
- ruleValue: ruleValue || 'pathname',
124
- url,
125
- date: Date.now(),
126
- mock, // 使用当前的 mock 状态(true)
127
- locked: false,
128
- mockVersion: null,
129
- mockTime: null,
130
- })
131
- console.log(`[mockbubu] ✅ 首次捕获文件到组 ${currentGroupId}: ${filename} | Mock: ${mock}`)
132
- } else {
133
- // 已存在配置,仅更新元数据(不覆盖 mock、locked 等用户配置)
134
- const existingGroupConfig = groupManager.getGroupFileConfig(currentGroupId, filename)
135
- groupManager.setGroupFileConfig(currentGroupId, filename, {
136
- ...existingGroupConfig,
137
- method,
138
- rule,
139
- status: session.res.statusCode,
140
- pattern,
141
- ruleValue: ruleValue || 'pathname',
142
- url,
143
- date: Date.now(),
144
- })
145
- console.log(`[mockbubu] 🔄 更新文件元数据到组 ${currentGroupId}: ${filename}`)
146
- }
147
-
148
- // 将文件写入组目录
149
- const tempSession = JSON.parse(JSON.stringify(session))
150
- tempSession.res.body = content
151
- const groupFilePath = `${currentGroupId}/${filename}`
152
- storage.writeFile(groupFilePath, JSON.stringify(tempSession))
153
- console.log(`[mockbubu] 💾 写入文件: ${groupFilePath}`)
154
-
155
- setApiListUpdated(storage, true)
156
- })
157
- }))
158
-
159
- // 将响应透传给客户端
160
- svrRes.pipe(res)
161
- })
162
-
163
- req.pipe(client)
164
- } else {
165
- // mock: false,已有缓存则直接透传,无缓存则捕获
166
- if (sessionCache) {
167
- console.log(`[mockbubu] 📤 直接透传(已有缓存): ${filename}`)
168
- return req.passThrough()
169
- }
170
-
171
- // 无缓存,捕获最新的接口数据
172
- console.log(`[mockbubu] 🎣 开始捕获(无缓存): ${filename}`)
173
- const client = req.request((svrRes) => {
174
- const encoding = svrRes.headers['content-encoding']
175
- let body
176
-
177
- svrRes.on('data', (data) => {
178
- body = body ? Buffer.concat([body, data]) : data
179
- })
180
- svrRes.on('end', withTryCatch(async () => {
181
- if (!body) return
182
-
183
- const content = await handleBuffer2String({ body, encoding })
184
- // 获取完整的抓包数据,要等待响应完成
185
- req.getSession(async (session) => {
186
- // 如果设置了 enable://hide 会获取到空数据
187
- if (!session) {
188
- return
189
- }
190
-
191
- // 完全隔离架构:将元数据和配置写入当前组
192
- // 检查组配置是否真实存在(而不是默认返回值)
193
- const configKey = groupManager.getGroupConfigKey(currentGroupId, filename)
194
- const isFirstCapture = !storage.hasProperty(configKey)
195
-
196
- if (isFirstCapture) {
197
- // 首次捕获:创建完整配置
198
- groupManager.setGroupFileConfig(currentGroupId, filename, {
199
- method,
200
- rule,
201
- status: session.res.statusCode,
202
- pattern,
203
- ruleValue: ruleValue || 'pathname',
204
- url,
205
- date: Date.now(),
206
- mock: false,
207
- locked: false,
208
- mockVersion: null,
209
- mockTime: null,
210
- })
211
- console.log(`[mockbubu] ✅ 首次捕获文件到组 ${currentGroupId}: ${filename}`)
212
- } else {
213
- // 已存在配置,仅更新元数据(不覆盖 mock、locked 等用户配置)
214
- const existingGroupConfig = groupManager.getGroupFileConfig(currentGroupId, filename)
215
- groupManager.setGroupFileConfig(currentGroupId, filename, {
216
- ...existingGroupConfig,
217
- method,
218
- rule,
219
- status: session.res.statusCode,
220
- pattern,
221
- ruleValue: ruleValue || 'pathname',
222
- url,
223
- date: Date.now(),
224
- })
225
- console.log(`[mockbubu] 🔄 更新文件元数据到组 ${currentGroupId}: ${filename}`)
226
- }
227
-
228
- // 将文件写入组目录
229
- const tempSession = JSON.parse(JSON.stringify(session))
230
- tempSession.res.body = content
231
- const groupFilePath = `${currentGroupId}/${filename}`
232
- storage.writeFile(groupFilePath, JSON.stringify(tempSession))
233
- console.log(`[mockbubu] 💾 写入文件: ${groupFilePath}`)
234
-
235
- setApiListUpdated(storage, true)
236
- })
237
- }))
238
-
239
- // 将响应透传给客户端
240
- svrRes.pipe(res)
241
- })
242
-
243
- req.pipe(client)
244
- }
245
- } catch (error) {
246
- req.passThrough()
247
- }
248
- })
249
- }
@@ -1,205 +0,0 @@
1
- const { createErrorResponse, createSuccessResponse } = require('../util')
2
- const {
3
- validateFilename,
4
- validateVersionName,
5
- validateMockData,
6
- validate,
7
- } = require('../validator')
8
-
9
-
10
- module.exports = (router) => {
11
- // 新增版本
12
- router.post('/cgi-bin/mockbubu/add-new-version', validate({
13
- name: validateFilename,
14
- versionName: validateVersionName,
15
- content: validateMockData,
16
- }), (ctx) => {
17
- try {
18
- const { groupManager } = ctx
19
- const { versionName, content = {}, name, description = '' } = ctx.request.body || {}
20
-
21
- console.log('[mockbubu] 创建版本:', { versionName, name, description })
22
-
23
- // 获取当前组ID,使用组级别的版本管理
24
- const currentGroupId = groupManager.getCurrentGroupId()
25
- groupManager.addGroupVersion(currentGroupId, name, versionName, content, description)
26
-
27
- // 立即读取已保存的版本,确认时间戳等信息
28
- const versions = groupManager.getGroupVersions(currentGroupId, name)
29
- const newVersion = versions.find(v => v.filename === versionName)
30
-
31
- console.log('[mockbubu] 创建后读取版本:', newVersion)
32
-
33
- ctx.body = createSuccessResponse(newVersion || {
34
- filename: versionName,
35
- content,
36
- description,
37
- createTime: Date.now(),
38
- updateTime: Date.now(),
39
- }, '版本创建成功')
40
- } catch (error) {
41
- ctx.body = createErrorResponse(error.message)
42
- }
43
- })
44
-
45
- // 删除版本
46
- router.post('/cgi-bin/mockbubu/delete-version', validate({
47
- name: validateFilename,
48
- versionName: validateVersionName,
49
- }), (ctx) => {
50
- try {
51
- const { groupManager } = ctx
52
- const { versionName, name } = ctx.request.body || {}
53
-
54
- // 获取当前组ID和配置
55
- const currentGroupId = groupManager.getCurrentGroupId()
56
- const groupConfig = groupManager.getGroupFileConfig(currentGroupId, name)
57
-
58
- // 如果删除的是当前mock版本,则清除mock版本设置
59
- if (versionName === groupConfig.mockVersion) {
60
- groupConfig.mockVersion = null
61
- groupManager.setGroupFileConfig(currentGroupId, name, groupConfig)
62
- }
63
-
64
- // 使用组级别的版本删除
65
- groupManager.deleteGroupVersion(currentGroupId, name, versionName)
66
-
67
- ctx.body = createSuccessResponse(null, '版本删除成功')
68
- } catch (error) {
69
- ctx.body = createErrorResponse(error.message)
70
- }
71
- })
72
-
73
- // 更新版本内容
74
- router.post('/cgi-bin/mockbubu/update-version-content', validate({
75
- name: validateFilename,
76
- versionName: validateVersionName,
77
- content: validateMockData,
78
- }), (ctx) => {
79
- try {
80
- const { groupManager } = ctx
81
- const { versionName, content, name } = ctx.request.body || {}
82
-
83
- // 获取当前组ID,使用组级别的版本内容更新
84
- const currentGroupId = groupManager.getCurrentGroupId()
85
- groupManager.updateGroupVersionContent(currentGroupId, name, versionName, content)
86
-
87
- ctx.body = createSuccessResponse(null, '版本内容更新成功')
88
- } catch (error) {
89
- ctx.body = createErrorResponse(error.message)
90
- }
91
- })
92
-
93
- // 更新版本名称(已废弃,使用 update-version-meta 代替)
94
- router.post('/cgi-bin/mockbubu/update-version-name', validate({
95
- name: validateFilename,
96
- versionName: validateVersionName,
97
- newVersion: validateVersionName,
98
- }), (ctx) => {
99
- try {
100
- const { groupManager } = ctx
101
- const { versionName, name, newVersion } = ctx.request.body || {}
102
-
103
- // 获取当前组ID,使用组级别的版本元信息更新
104
- const currentGroupId = groupManager.getCurrentGroupId()
105
- const groupConfig = groupManager.getGroupFileConfig(currentGroupId, name)
106
-
107
- // 获取原描述
108
- const versions = groupManager.getGroupVersions(currentGroupId, name)
109
- const oldVersion = versions.find(v => v.filename === versionName)
110
- const description = oldVersion?.description || ''
111
-
112
- groupManager.updateGroupVersionMeta(currentGroupId, name, versionName, newVersion, description)
113
-
114
- // 如果更新的是当前mock版本,需要更新mockVersion属性
115
- if (versionName === groupConfig.mockVersion && versionName !== newVersion) {
116
- groupConfig.mockVersion = newVersion
117
- groupManager.setGroupFileConfig(currentGroupId, name, groupConfig)
118
- }
119
-
120
- ctx.body = createSuccessResponse(null, '版本名称更新成功')
121
- } catch (error) {
122
- ctx.body = createErrorResponse(error.message)
123
- }
124
- })
125
-
126
- // 获取版本列表
127
- router.post('/cgi-bin/mockbubu/get-versions', validate({
128
- name: validateFilename,
129
- }), (ctx) => {
130
- try {
131
- const { groupManager } = ctx
132
- const { name } = ctx.request.body || {}
133
-
134
- // 获取当前组ID,使用组级别的版本列表获取
135
- const currentGroupId = groupManager.getCurrentGroupId()
136
- const list = groupManager.getGroupVersions(currentGroupId, name) || []
137
-
138
- console.log('[mockbubu] 获取版本列表:', name, 'group:', currentGroupId, 'count:', list.length)
139
- if (list.length > 0) {
140
- console.log('[mockbubu] 第一个版本示例:', list[0])
141
- }
142
-
143
- ctx.body = createSuccessResponse(list, '获取版本列表成功')
144
- } catch (error) {
145
- ctx.body = createErrorResponse(error.message)
146
- }
147
- })
148
-
149
- // 设置mock的版本
150
- router.post('/cgi-bin/mockbubu/set-mock-version', validate({
151
- name: validateFilename,
152
- versionName: (val) => {
153
- // versionName 可以为空字符串(表示清除版本)
154
- if (val === '') return null
155
- return validateVersionName(val)
156
- },
157
- }), (ctx) => {
158
- try {
159
- const { groupManager } = ctx
160
- const { name, versionName } = ctx.request.body || {}
161
-
162
- // 获取当前组ID和配置
163
- const currentGroupId = groupManager.getCurrentGroupId()
164
- const groupConfig = groupManager.getGroupFileConfig(currentGroupId, name)
165
-
166
- // 更新 mockVersion
167
- groupConfig.mockVersion = versionName || null
168
- groupManager.setGroupFileConfig(currentGroupId, name, groupConfig)
169
-
170
- ctx.body = createSuccessResponse(null, 'Mock版本设置成功')
171
- } catch (error) {
172
- ctx.body = createErrorResponse(error.message)
173
- }
174
- })
175
-
176
- // 更新版本元信息(名称和描述)
177
- router.post('/cgi-bin/mockbubu/update-version-meta', validate({
178
- name: validateFilename,
179
- versionName: validateVersionName,
180
- newVersionName: validateVersionName,
181
- }), (ctx) => {
182
- try {
183
- const { groupManager } = ctx
184
- const { name, versionName, newVersionName, description = '' } = ctx.request.body || {}
185
-
186
- console.log('[mockbubu] 更新版本元信息:', { name, versionName, newVersionName, description })
187
-
188
- // 获取当前组ID,使用组级别的版本元信息更新
189
- const currentGroupId = groupManager.getCurrentGroupId()
190
- groupManager.updateGroupVersionMeta(currentGroupId, name, versionName, newVersionName, description)
191
-
192
- // 如果更新的是当前mock版本,需要更新mockVersion属性
193
- const groupConfig = groupManager.getGroupFileConfig(currentGroupId, name)
194
- if (versionName === groupConfig.mockVersion && versionName !== newVersionName) {
195
- groupConfig.mockVersion = newVersionName
196
- groupManager.setGroupFileConfig(currentGroupId, name, groupConfig)
197
- }
198
-
199
- ctx.body = createSuccessResponse(null, '版本信息更新成功')
200
- } catch (error) {
201
- console.error('[mockbubu] 更新版本元信息失败:', error)
202
- ctx.body = createErrorResponse(error.message)
203
- }
204
- })
205
- }
@@ -1,153 +0,0 @@
1
- const {
2
- getApiList,
3
- getGroupFileList,
4
- getProperty,
5
- } = require('../utils')
6
- const { RangeFilterMap, RuleFilterMap, LockedFilterMap, MethodFilterMap, StatusFilterMap } = require('../const')
7
-
8
- // 统一错误响应格式
9
- const createErrorResponse = (message, code = 500) => ({
10
- code,
11
- msg: message || '操作失败',
12
- })
13
-
14
- // 统一成功响应格式
15
- const createSuccessResponse = (data = null, message = '操作成功') => ({
16
- code: 200,
17
- msg: message,
18
- ...(data && { data }),
19
- })
20
-
21
- // 执行筛选
22
- const execFilter = (result, filter) => {
23
- const { key, value, method } = filter
24
- const list = result.filter((item) => {
25
- const type = Object.prototype.toString.call(value)
26
-
27
- switch (method) {
28
- case 'indexOf':
29
- if (type === '[object String]') {
30
- return ~item[key].indexOf(value)
31
- }
32
- break
33
- case 'equal':
34
- return item[key] === value
35
- case 'unequal':
36
- return item[key] !== value
37
- case 'range':
38
- // 范围筛选,value 是 [min, max] 数组
39
- if (Array.isArray(value) && value.length === 2) {
40
- const itemValue = item[key]
41
- return itemValue >= value[0] && itemValue <= value[1]
42
- }
43
- break
44
- }
45
-
46
- return true
47
- })
48
-
49
- return list
50
- }
51
-
52
- // 处理筛选条件
53
- const handleFilterList = (list, filterOptions) => {
54
- if (!Array.isArray(list) || !filterOptions) return []
55
-
56
- const filters = []
57
- const { name, rule, range, ruleValue, locked, method, status } = filterOptions
58
- if (name) {
59
- filters.push({
60
- key: 'name',
61
- value: name.trim(),
62
- method: 'indexOf',
63
- })
64
- }
65
-
66
- if (rule) {
67
- filters.push({
68
- key: 'rule',
69
- value: rule.trim(),
70
- method: 'equal',
71
- })
72
- }
73
-
74
- // 范围筛选
75
- RangeFilterMap[range] && filters.push(RangeFilterMap[range])
76
- // 规则值筛选
77
- RuleFilterMap[ruleValue] && filters.push(RuleFilterMap[ruleValue])
78
- // 锁定筛选
79
- LockedFilterMap[locked] && filters.push(LockedFilterMap[locked])
80
- // HTTP Method 筛选
81
- MethodFilterMap[method] && filters.push(MethodFilterMap[method])
82
- // 状态码筛选
83
- StatusFilterMap[status] && filters.push(StatusFilterMap[status])
84
-
85
- // 执行筛选
86
- const filteredList = filters.reduce((result, filter) => {
87
- return execFilter(result, filter)
88
- }, list)
89
-
90
- return filteredList
91
- }
92
-
93
- /**
94
- * 过滤组配置,只保留组级别的字段
95
- * 移除全局元数据字段 (query, payload, method, rule, status, pattern, ruleValue, url, date)
96
- */
97
- const filterGroupConfig = (groupConfig) => {
98
- if (!groupConfig) return {}
99
- const validKeys = ['mock', 'locked', 'mockVersion', 'mockTime']
100
- const filtered = {}
101
-
102
- Object.keys(groupConfig).forEach(key => {
103
- // 保留基础配置字段
104
- if (validKeys.includes(key)) {
105
- filtered[key] = groupConfig[key]
106
- } else if (key.startsWith('version.') || key.startsWith('versionMeta.')) {
107
- // 保留版本相关字段
108
- filtered[key] = groupConfig[key]
109
- }
110
- // 其他字段(如 query, payload 等全局元数据)被过滤掉
111
- })
112
-
113
- return filtered
114
- }
115
-
116
- // 获取文件列表(包含文件属性数据)
117
- // 完全隔离架构:直接获取当前组的文件列表
118
- const getFullDataList = (localStorage, groupManager, groupId) => {
119
- if (!groupManager || !groupId) {
120
- // 向后兼容:如果没有提供 groupManager,使用旧逻辑
121
- const list = getApiList(localStorage) || []
122
- return list.map((item) => {
123
- const props = getProperty(localStorage, item.name) || {}
124
- delete item.data
125
- return { ...item, ...props }
126
- })
127
- }
128
-
129
- // 新逻辑:直接获取当前组的文件列表
130
- const list = getGroupFileList(localStorage, groupId) || []
131
- const result = []
132
-
133
- list.forEach((item) => {
134
- // 读取组元数据(已合并全局元数据 + 组配置)
135
- const config = groupManager.getGroupFileConfig(groupId, item.name)
136
-
137
- if (config) {
138
- result.push({
139
- name: item.name,
140
- ...config, // 包含 url, method, date, status, rule, mock, locked 等
141
- })
142
- }
143
- })
144
-
145
- return result
146
- }
147
-
148
- exports.createErrorResponse = createErrorResponse
149
- exports.createSuccessResponse = createSuccessResponse
150
- exports.execFilter = execFilter
151
- exports.handleFilterList = handleFilterList
152
- exports.filterGroupConfig = filterGroupConfig
153
- exports.getFullDataList = getFullDataList