aicodeswitch 3.0.1 → 3.0.3

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.
@@ -8,6 +8,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
11
22
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
23
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
24
  };
@@ -26,7 +37,7 @@ class FileSystemDatabaseManager {
26
37
  get vendorsFile() { return path_1.default.join(this.dataPath, 'vendors.json'); }
27
38
  get servicesFile() { return path_1.default.join(this.dataPath, 'services.json'); }
28
39
  get routesFile() { return path_1.default.join(this.dataPath, 'routes.json'); }
29
- get rulesFile() { return path_1.default.join(this.dataPath, 'rules.json'); }
40
+ get rulesFile() { return path_1.default.join(this.dataPath, 'rules.json'); } // legacy
30
41
  get configFile() { return path_1.default.join(this.dataPath, 'config.json'); }
31
42
  get sessionsFile() { return path_1.default.join(this.dataPath, 'sessions.json'); }
32
43
  get logsDir() { return path_1.default.join(this.dataPath, 'logs'); }
@@ -76,12 +87,7 @@ class FileSystemDatabaseManager {
76
87
  writable: true,
77
88
  value: []
78
89
  });
79
- Object.defineProperty(this, "apiServices", {
80
- enumerable: true,
81
- configurable: true,
82
- writable: true,
83
- value: []
84
- });
90
+ // 移除独立的 apiServices 存储,现在作为 vendor 的属性
85
91
  Object.defineProperty(this, "routes", {
86
92
  enumerable: true,
87
93
  configurable: true,
@@ -131,6 +137,18 @@ class FileSystemDatabaseManager {
131
137
  writable: true,
132
138
  value: this.createEmptyStatistics()
133
139
  });
140
+ Object.defineProperty(this, "contentTypeDistributionInitialized", {
141
+ enumerable: true,
142
+ configurable: true,
143
+ writable: true,
144
+ value: false
145
+ });
146
+ Object.defineProperty(this, "contentTypeDistributionInitializing", {
147
+ enumerable: true,
148
+ configurable: true,
149
+ writable: true,
150
+ value: false
151
+ });
134
152
  // 缓存机制
135
153
  Object.defineProperty(this, "logsCountCache", {
136
154
  enumerable: true,
@@ -178,10 +196,9 @@ class FileSystemDatabaseManager {
178
196
  loadAllData() {
179
197
  return __awaiter(this, void 0, void 0, function* () {
180
198
  yield Promise.all([
181
- this.loadVendors(),
182
- this.loadServices(),
199
+ this.loadVendors(), // loadVendors 内部会处理旧 services.json 的迁移
200
+ // 删除: this.loadServices(),
183
201
  this.loadRoutes(),
184
- this.loadRules(),
185
202
  this.loadConfig(),
186
203
  this.loadSessions(),
187
204
  this.loadLogsIndex(),
@@ -200,59 +217,172 @@ class FileSystemDatabaseManager {
200
217
  catch (_a) {
201
218
  this.vendors = [];
202
219
  }
220
+ // 兼容性检查:如果存在旧的 services.json,自动迁移
221
+ yield this.migrateServicesIfNeeded();
203
222
  });
204
223
  }
205
- saveVendors() {
206
- return __awaiter(this, void 0, void 0, function* () {
207
- yield promises_1.default.writeFile(this.vendorsFile, JSON.stringify(this.vendors, null, 2));
208
- });
209
- }
210
- loadServices() {
224
+ /**
225
+ * 检测并迁移旧的 services.json 到新结构
226
+ * 旧格式:vendors.json 和 services.json 分离
227
+ * 新格式:vendors.json 包含嵌套的 services 数组
228
+ */
229
+ migrateServicesIfNeeded() {
211
230
  return __awaiter(this, void 0, void 0, function* () {
231
+ const oldServicesFile = this.servicesFile;
212
232
  try {
213
- const data = yield promises_1.default.readFile(this.servicesFile, 'utf-8');
214
- this.apiServices = JSON.parse(data);
233
+ yield promises_1.default.access(oldServicesFile);
234
+ console.log('[Database] 发现旧的 services.json 文件,开始迁移到新结构...');
235
+ // 读取旧服务数据
236
+ const servicesData = yield promises_1.default.readFile(oldServicesFile, 'utf-8');
237
+ const oldServices = JSON.parse(servicesData);
238
+ console.log(`[Database] 准备迁移 ${oldServices.length} 个服务...`);
239
+ // 按 vendorId 分组
240
+ const servicesByVendor = new Map();
241
+ for (const service of oldServices) {
242
+ if (!service.vendorId) {
243
+ console.warn(`[Database] 跳过没有 vendorId 的服务: ${service.id}`);
244
+ continue;
245
+ }
246
+ if (!servicesByVendor.has(service.vendorId)) {
247
+ servicesByVendor.set(service.vendorId, []);
248
+ }
249
+ // 移除 vendorId 字段,因为现在通过父级关系隐式关联
250
+ const { vendorId } = service, serviceWithoutVendorId = __rest(service, ["vendorId"]);
251
+ servicesByVendor.get(service.vendorId).push(serviceWithoutVendorId);
252
+ }
253
+ // 合并到 vendors 数组
254
+ let migratedCount = 0;
255
+ for (const vendor of this.vendors) {
256
+ const services = servicesByVendor.get(vendor.id);
257
+ if (services) {
258
+ vendor.services = services;
259
+ migratedCount += services.length;
260
+ }
261
+ else {
262
+ vendor.services = [];
263
+ }
264
+ }
265
+ // 保存新的 vendors.json
266
+ yield this.saveVendors();
267
+ console.log(`[Database] 迁移完成:${migratedCount} 个服务已迁移`);
215
268
  }
216
- catch (_a) {
217
- this.apiServices = [];
269
+ catch (err) {
270
+ if (err.code === 'ENOENT') {
271
+ // 旧文件不存在,这是正常的(新安装或已迁移)
272
+ return;
273
+ }
274
+ console.error('[Database] 迁移 services 时出错:', err);
218
275
  }
219
276
  });
220
277
  }
221
- saveServices() {
278
+ saveVendors() {
222
279
  return __awaiter(this, void 0, void 0, function* () {
223
- yield promises_1.default.writeFile(this.servicesFile, JSON.stringify(this.apiServices, null, 2));
280
+ // 确保每个供应商都有 services 数组
281
+ const normalizedVendors = this.vendors.map(v => (Object.assign(Object.assign({}, v), { services: v.services || [] })));
282
+ yield promises_1.default.writeFile(this.vendorsFile, JSON.stringify(normalizedVendors, null, 2));
224
283
  });
225
284
  }
285
+ // loadServices 和 saveServices 已移除
286
+ // 服务现在作为供应商的属性存储在 vendors.json 中
287
+ // 迁移逻辑见 migrateServicesIfNeeded() 方法
226
288
  loadRoutes() {
227
289
  return __awaiter(this, void 0, void 0, function* () {
290
+ let routesFileFormat = 'missing';
291
+ let routesFromFile = [];
292
+ let rulesFromFile = [];
293
+ let hasRulesInRoutesFile = false;
228
294
  try {
229
295
  const data = yield promises_1.default.readFile(this.routesFile, 'utf-8');
230
- this.routes = JSON.parse(data);
296
+ const parsed = JSON.parse(data);
297
+ if (Array.isArray(parsed)) {
298
+ routesFileFormat = 'array';
299
+ routesFromFile = parsed;
300
+ }
301
+ else if (parsed && typeof parsed === 'object') {
302
+ routesFileFormat = 'combined';
303
+ routesFromFile = Array.isArray(parsed.routes) ? parsed.routes : [];
304
+ if (Array.isArray(parsed.rules)) {
305
+ rulesFromFile = parsed.rules;
306
+ hasRulesInRoutesFile = true;
307
+ }
308
+ }
309
+ else {
310
+ routesFileFormat = 'unknown';
311
+ }
231
312
  }
232
313
  catch (_a) {
233
- this.routes = [];
314
+ routesFileFormat = 'missing';
234
315
  }
316
+ this.routes = routesFromFile;
317
+ this.rules = rulesFromFile;
318
+ // 兼容旧的 rules.json 文件(迁移到 routes.json 的 rules 属性)
319
+ yield this.migrateRulesIfNeeded(routesFileFormat, hasRulesInRoutesFile);
235
320
  });
236
321
  }
237
- saveRoutes() {
322
+ saveRoutesData() {
238
323
  return __awaiter(this, void 0, void 0, function* () {
239
- yield promises_1.default.writeFile(this.routesFile, JSON.stringify(this.routes, null, 2));
324
+ const payload = {
325
+ routes: this.routes,
326
+ rules: this.rules,
327
+ };
328
+ yield promises_1.default.writeFile(this.routesFile, JSON.stringify(payload, null, 2));
240
329
  });
241
330
  }
242
- loadRules() {
331
+ saveRoutes() {
243
332
  return __awaiter(this, void 0, void 0, function* () {
244
- try {
245
- const data = yield promises_1.default.readFile(this.rulesFile, 'utf-8');
246
- this.rules = JSON.parse(data);
247
- }
248
- catch (_a) {
249
- this.rules = [];
250
- }
333
+ yield this.saveRoutesData();
251
334
  });
252
335
  }
253
336
  saveRules() {
254
337
  return __awaiter(this, void 0, void 0, function* () {
255
- yield promises_1.default.writeFile(this.rulesFile, JSON.stringify(this.rules, null, 2));
338
+ yield this.saveRoutesData();
339
+ });
340
+ }
341
+ /**
342
+ * 检测并迁移旧的 rules.json 到 routes.json 的 rules 属性
343
+ * 旧格式:routes.json + rules.json 分离
344
+ * 新格式:routes.json 内包含 { routes, rules }
345
+ */
346
+ migrateRulesIfNeeded(routesFileFormat, hasRulesInRoutesFile) {
347
+ return __awaiter(this, void 0, void 0, function* () {
348
+ const oldRulesFile = this.rulesFile;
349
+ const oldRulesExists = yield promises_1.default.access(oldRulesFile)
350
+ .then(() => true)
351
+ .catch(() => false);
352
+ let merged = false;
353
+ if (oldRulesExists) {
354
+ try {
355
+ const data = yield promises_1.default.readFile(oldRulesFile, 'utf-8');
356
+ const oldRules = JSON.parse(data);
357
+ if (Array.isArray(oldRules)) {
358
+ if (this.rules.length > 0) {
359
+ const mergedMap = new Map();
360
+ oldRules.forEach((rule, index) => {
361
+ const key = (rule === null || rule === void 0 ? void 0 : rule.id) || `legacy-${index}`;
362
+ if (!mergedMap.has(key)) {
363
+ mergedMap.set(key, rule);
364
+ }
365
+ });
366
+ this.rules.forEach((rule, index) => {
367
+ const key = (rule === null || rule === void 0 ? void 0 : rule.id) || `current-${index}`;
368
+ mergedMap.set(key, rule);
369
+ });
370
+ this.rules = Array.from(mergedMap.values());
371
+ }
372
+ else {
373
+ this.rules = oldRules;
374
+ }
375
+ merged = true;
376
+ }
377
+ }
378
+ catch (error) {
379
+ console.error('[Database] 迁移 rules.json 时出错:', error);
380
+ }
381
+ }
382
+ // 如果 routes.json 还是旧格式/缺失,或从旧 rules.json 合并过数据,或缺少 rules 字段,则写入新格式
383
+ if (routesFileFormat !== 'combined' || merged || (routesFileFormat === 'combined' && !hasRulesInRoutesFile)) {
384
+ yield this.saveRoutesData();
385
+ }
256
386
  });
257
387
  }
258
388
  loadConfig() {
@@ -387,10 +517,6 @@ class FileSystemDatabaseManager {
387
517
  // 保存索引
388
518
  yield this.saveLogsIndex();
389
519
  console.log(`[Database] Successfully migrated ${migratedCount} log entries to ${this.logShardsIndex.length} shard(s)`);
390
- // 备份旧文件
391
- const backupFile = path_1.default.join(this.dataPath, 'logs.json.backup');
392
- yield promises_1.default.rename(oldLogsFile, backupFile);
393
- console.log(`[Database] Old logs.json backed up to ${backupFile}`);
394
520
  }
395
521
  catch (err) {
396
522
  if (err.code === 'ENOENT') {
@@ -533,9 +659,11 @@ class FileSystemDatabaseManager {
533
659
  try {
534
660
  const data = yield promises_1.default.readFile(this.statisticsFile, 'utf-8');
535
661
  this.statistics = JSON.parse(data);
662
+ this.contentTypeDistributionInitialized = this.statistics.contentTypeDistribution.length > 0;
536
663
  }
537
664
  catch (_a) {
538
665
  this.statistics = this.createEmptyStatistics();
666
+ this.contentTypeDistributionInitialized = false;
539
667
  // 创建空文件
540
668
  yield this.saveStatistics();
541
669
  }
@@ -573,11 +701,17 @@ class FileSystemDatabaseManager {
573
701
  return b.createdAt - a.createdAt;
574
702
  });
575
703
  }
704
+ // 新增:获取单个供应商(带服务)
705
+ getVendor(id) {
706
+ return this.vendors.find(v => v.id === id);
707
+ }
576
708
  createVendor(vendor) {
577
709
  return __awaiter(this, void 0, void 0, function* () {
710
+ console.log('[数据库] 创建供应商,输入数据:', JSON.stringify(vendor, null, 2));
578
711
  const id = crypto_1.default.randomUUID();
579
712
  const now = Date.now();
580
- const newVendor = Object.assign(Object.assign({}, vendor), { id, createdAt: now, updatedAt: now });
713
+ const newVendor = Object.assign(Object.assign({}, vendor), { id, services: vendor.services || [], createdAt: now, updatedAt: now });
714
+ console.log('[数据库] 创建供应商,返回数据:', JSON.stringify(newVendor, null, 2));
581
715
  this.vendors.push(newVendor);
582
716
  yield this.saveVendors();
583
717
  return newVendor;
@@ -589,7 +723,7 @@ class FileSystemDatabaseManager {
589
723
  if (index === -1)
590
724
  return false;
591
725
  const now = Date.now();
592
- this.vendors[index] = Object.assign(Object.assign(Object.assign({}, this.vendors[index]), vendor), { id, updatedAt: now });
726
+ this.vendors[index] = Object.assign(Object.assign(Object.assign({}, this.vendors[index]), vendor), { id, services: vendor.services !== undefined ? vendor.services : this.vendors[index].services, updatedAt: now });
593
727
  yield this.saveVendors();
594
728
  return true;
595
729
  });
@@ -599,9 +733,13 @@ class FileSystemDatabaseManager {
599
733
  const index = this.vendors.findIndex(v => v.id === id);
600
734
  if (index === -1)
601
735
  return false;
602
- // 删除关联的服务
603
- this.apiServices = this.apiServices.filter(s => s.vendorId !== id);
604
- yield this.saveServices();
736
+ // 检查是否有服务被规则使用
737
+ const vendor = this.vendors[index];
738
+ const serviceIds = (vendor.services || []).map(s => s.id);
739
+ const rulesUsingServices = this.rules.filter(r => serviceIds.includes(r.targetServiceId));
740
+ if (rulesUsingServices.length > 0) {
741
+ throw new Error(`无法删除供应商:有 ${rulesUsingServices.length} 个规则正在使用该供应商的服务`);
742
+ }
605
743
  this.vendors.splice(index, 1);
606
744
  yield this.saveVendors();
607
745
  return true;
@@ -609,29 +747,89 @@ class FileSystemDatabaseManager {
609
747
  }
610
748
  // API Service operations
611
749
  getAPIServices(vendorId) {
612
- const services = vendorId
613
- ? this.apiServices.filter(s => s.vendorId === vendorId)
614
- : this.apiServices;
615
- return services.sort((a, b) => b.createdAt - a.createdAt);
750
+ if (vendorId) {
751
+ const vendor = this.vendors.find(v => v.id === vendorId);
752
+ if (!vendor)
753
+ return [];
754
+ // 返回指定供应商的服务,并添加 vendorId
755
+ return (vendor.services || []).map(service => (Object.assign(Object.assign({}, service), { vendorId: vendor.id // 添加 vendorId 以便前端使用
756
+ })));
757
+ }
758
+ // 返回所有供应商的所有服务(扁平化),并添加 vendorId
759
+ const allServices = [];
760
+ for (const vendor of this.vendors) {
761
+ if (vendor.services) {
762
+ const servicesWithVendorId = vendor.services.map(service => (Object.assign(Object.assign({}, service), { vendorId: vendor.id // 添加 vendorId 以便前端使用
763
+ })));
764
+ allServices.push(...servicesWithVendorId);
765
+ }
766
+ }
767
+ return allServices.sort((a, b) => b.createdAt - a.createdAt);
768
+ }
769
+ // 新增:通过 ID 获取服务
770
+ getAPIService(id) {
771
+ var _a;
772
+ for (const vendor of this.vendors) {
773
+ const service = (_a = vendor.services) === null || _a === void 0 ? void 0 : _a.find(s => s.id === id);
774
+ if (service) {
775
+ return service;
776
+ }
777
+ }
778
+ return undefined;
779
+ }
780
+ // 新增:获取服务所属的供应商
781
+ getVendorByServiceId(serviceId) {
782
+ var _a;
783
+ for (const vendor of this.vendors) {
784
+ if ((_a = vendor.services) === null || _a === void 0 ? void 0 : _a.some(s => s.id === serviceId)) {
785
+ return vendor;
786
+ }
787
+ }
788
+ return undefined;
616
789
  }
617
790
  createAPIService(service) {
618
791
  return __awaiter(this, void 0, void 0, function* () {
792
+ console.log('[数据库] 创建服务,输入数据:', JSON.stringify(service, null, 2));
793
+ // 从 vendorId 找到供应商
794
+ const vendorId = service.vendorId;
795
+ if (!vendorId) {
796
+ throw new Error('创建服务时必须提供 vendorId');
797
+ }
798
+ const vendor = this.vendors.find(v => v.id === vendorId);
799
+ if (!vendor) {
800
+ throw new Error(`供应商不存在: ${vendorId}`);
801
+ }
802
+ // 移除 vendorId 字段(数据存储时不需要)
803
+ const _a = service, { vendorId: _ } = _a, serviceData = __rest(_a, ["vendorId"]);
619
804
  const id = crypto_1.default.randomUUID();
620
805
  const now = Date.now();
621
- const newService = Object.assign(Object.assign({}, service), { id, createdAt: now, updatedAt: now });
622
- this.apiServices.push(newService);
623
- yield this.saveServices();
624
- return newService;
806
+ const newService = Object.assign(Object.assign({}, serviceData), { id, createdAt: now, updatedAt: now });
807
+ console.log('[数据库] 创建服务,最终数据:', JSON.stringify(newService, null, 2));
808
+ if (!vendor.services) {
809
+ vendor.services = [];
810
+ }
811
+ vendor.services.push(newService);
812
+ // 更新供应商的 updatedAt 时间
813
+ vendor.updatedAt = now;
814
+ yield this.saveVendors();
815
+ console.log('[数据库] 服务已保存,当前总数:', vendor.services.length);
816
+ return Object.assign(Object.assign({}, newService), { vendorId });
625
817
  });
626
818
  }
627
819
  updateAPIService(id, service) {
628
820
  return __awaiter(this, void 0, void 0, function* () {
629
- const index = this.apiServices.findIndex(s => s.id === id);
821
+ // 查找服务所属的供应商
822
+ const vendor = this.getVendorByServiceId(id);
823
+ if (!vendor)
824
+ return false;
825
+ const index = vendor.services.findIndex(s => s.id === id);
630
826
  if (index === -1)
631
827
  return false;
632
828
  const now = Date.now();
633
- this.apiServices[index] = Object.assign(Object.assign(Object.assign({}, this.apiServices[index]), service), { id, updatedAt: now });
634
- yield this.saveServices();
829
+ vendor.services[index] = Object.assign(Object.assign(Object.assign({}, vendor.services[index]), service), { id, updatedAt: now });
830
+ // 更新供应商的 updatedAt 时间
831
+ vendor.updatedAt = now;
832
+ yield this.saveVendors();
635
833
  // 同步规则的超量限制
636
834
  yield this.syncRulesWithServiceLimits(id, service);
637
835
  return true;
@@ -639,14 +837,22 @@ class FileSystemDatabaseManager {
639
837
  }
640
838
  deleteAPIService(id) {
641
839
  return __awaiter(this, void 0, void 0, function* () {
642
- const index = this.apiServices.findIndex(s => s.id === id);
840
+ // 查找服务所属的供应商
841
+ const vendor = this.getVendorByServiceId(id);
842
+ if (!vendor)
843
+ return false;
844
+ const index = vendor.services.findIndex(s => s.id === id);
643
845
  if (index === -1)
644
846
  return false;
645
- // 删除关联的规则
646
- this.rules = this.rules.filter(r => r.targetServiceId !== id);
647
- yield this.saveRules();
648
- this.apiServices.splice(index, 1);
649
- yield this.saveServices();
847
+ // 检查是否有规则正在使用此服务
848
+ const rulesUsingService = this.rules.filter(r => r.targetServiceId === id);
849
+ if (rulesUsingService.length > 0) {
850
+ throw new Error(`无法删除服务:有 ${rulesUsingService.length} 个规则正在使用此服务`);
851
+ }
852
+ vendor.services.splice(index, 1);
853
+ // 更新供应商的 updatedAt 时间
854
+ vendor.updatedAt = Date.now();
855
+ yield this.saveVendors();
650
856
  return true;
651
857
  });
652
858
  }
@@ -656,7 +862,7 @@ class FileSystemDatabaseManager {
656
862
  if (relatedRules.length === 0)
657
863
  return;
658
864
  const now = Date.now();
659
- const currentService = this.apiServices.find(s => s.id === serviceId);
865
+ const currentService = this.getAPIService(serviceId);
660
866
  if (!currentService)
661
867
  return;
662
868
  let updated = false;
@@ -952,7 +1158,8 @@ class FileSystemDatabaseManager {
952
1158
  addLog(log) {
953
1159
  return __awaiter(this, void 0, void 0, function* () {
954
1160
  const id = crypto_1.default.randomUUID();
955
- const logWithId = Object.assign(Object.assign({}, log), { id });
1161
+ const contentType = this.resolveLogContentType(log);
1162
+ const logWithId = Object.assign(Object.assign({}, log), { contentType, id });
956
1163
  // 获取目标分片文件名
957
1164
  const filename = yield this.getLogShardFilename(logWithId.timestamp);
958
1165
  // 加载现有分片数据
@@ -1158,11 +1365,20 @@ class FileSystemDatabaseManager {
1158
1365
  // Export/Import operations
1159
1366
  exportData(password) {
1160
1367
  return __awaiter(this, void 0, void 0, function* () {
1368
+ // 扁平化所有服务(兼容旧格式)
1369
+ const allServices = [];
1370
+ for (const vendor of this.vendors) {
1371
+ if (vendor.services) {
1372
+ // 为每个服务添加 vendorId(兼容旧格式)
1373
+ const servicesWithVendorId = vendor.services.map(s => (Object.assign(Object.assign({}, s), { vendorId: vendor.id })));
1374
+ allServices.push(...servicesWithVendorId);
1375
+ }
1376
+ }
1161
1377
  const exportData = {
1162
- version: '1.0.0',
1378
+ version: '2.0.0', // 更新版本号
1163
1379
  exportDate: Date.now(),
1164
1380
  vendors: this.vendors,
1165
- apiServices: this.apiServices,
1381
+ apiServices: allServices, // 兼容旧格式
1166
1382
  routes: this.routes,
1167
1383
  rules: this.rules,
1168
1384
  config: this.config,
@@ -1178,16 +1394,36 @@ class FileSystemDatabaseManager {
1178
1394
  const decrypted = crypto_js_1.default.AES.decrypt(encryptedData, password);
1179
1395
  const jsonData = decrypted.toString(crypto_js_1.default.enc.Utf8);
1180
1396
  const importData = JSON.parse(jsonData);
1181
- this.vendors = importData.vendors;
1182
- this.apiServices = importData.apiServices;
1397
+ // 检测数据版本
1398
+ const isNewFormat = importData.vendors.some((v) => v.services);
1399
+ if (isNewFormat || importData.version >= '2.0.0') {
1400
+ // 新格式:直接使用 vendors(已包含 services)
1401
+ this.vendors = importData.vendors;
1402
+ }
1403
+ else {
1404
+ // 旧格式:需要重建 services 关系
1405
+ const { vendors, apiServices } = importData;
1406
+ // 构建 vendorId -> services 映射
1407
+ const servicesByVendor = new Map();
1408
+ for (const service of apiServices) {
1409
+ if (!service.vendorId)
1410
+ continue;
1411
+ if (!servicesByVendor.has(service.vendorId)) {
1412
+ servicesByVendor.set(service.vendorId, []);
1413
+ }
1414
+ const { vendorId } = service, serviceWithoutVendorId = __rest(service, ["vendorId"]);
1415
+ servicesByVendor.get(service.vendorId).push(serviceWithoutVendorId);
1416
+ }
1417
+ // 合并到 vendors
1418
+ this.vendors = vendors.map((vendor) => (Object.assign(Object.assign({}, vendor), { services: servicesByVendor.get(vendor.id) || [] })));
1419
+ }
1183
1420
  this.routes = importData.routes;
1184
1421
  this.rules = importData.rules;
1185
1422
  this.config = importData.config;
1186
1423
  yield Promise.all([
1187
1424
  this.saveVendors(),
1188
- this.saveServices(),
1425
+ // 删除: this.saveServices(),
1189
1426
  this.saveRoutes(),
1190
- this.saveRules(),
1191
1427
  this.saveConfig(),
1192
1428
  ]);
1193
1429
  return true;
@@ -1198,6 +1434,79 @@ class FileSystemDatabaseManager {
1198
1434
  }
1199
1435
  });
1200
1436
  }
1437
+ inferContentTypeFromLog(log) {
1438
+ var _a, _b;
1439
+ const requestModel = ((_a = log.requestModel) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || '';
1440
+ let bodyText = '';
1441
+ if (typeof log.body === 'string') {
1442
+ bodyText = log.body;
1443
+ }
1444
+ else if (log.body !== undefined) {
1445
+ try {
1446
+ bodyText = JSON.stringify(log.body);
1447
+ }
1448
+ catch (_c) {
1449
+ bodyText = '';
1450
+ }
1451
+ }
1452
+ const lowerBody = bodyText.toLowerCase();
1453
+ if (lowerBody.includes('image') || lowerBody.includes('base64')) {
1454
+ return 'image-understanding';
1455
+ }
1456
+ if (requestModel.includes('think')) {
1457
+ return 'thinking';
1458
+ }
1459
+ if ((((_b = log.usage) === null || _b === void 0 ? void 0 : _b.inputTokens) || 0) > 12000) {
1460
+ return 'long-context';
1461
+ }
1462
+ return 'default';
1463
+ }
1464
+ resolveLogContentType(log) {
1465
+ if (log.contentType) {
1466
+ return log.contentType;
1467
+ }
1468
+ if (log.ruleId) {
1469
+ const rule = this.getRule(log.ruleId);
1470
+ if (rule === null || rule === void 0 ? void 0 : rule.contentType) {
1471
+ return rule.contentType;
1472
+ }
1473
+ }
1474
+ return this.inferContentTypeFromLog(log);
1475
+ }
1476
+ ensureContentTypeDistribution() {
1477
+ return __awaiter(this, void 0, void 0, function* () {
1478
+ if (this.contentTypeDistributionInitialized || this.contentTypeDistributionInitializing) {
1479
+ return;
1480
+ }
1481
+ this.contentTypeDistributionInitializing = true;
1482
+ try {
1483
+ if (this.logShardsIndex.length === 0) {
1484
+ this.contentTypeDistributionInitialized = true;
1485
+ return;
1486
+ }
1487
+ const counts = new Map();
1488
+ let totalRequests = 0;
1489
+ for (const shard of this.logShardsIndex) {
1490
+ const shardLogs = yield this.loadLogShard(shard.filename);
1491
+ for (const log of shardLogs) {
1492
+ totalRequests++;
1493
+ const contentType = this.resolveLogContentType(log);
1494
+ counts.set(contentType, (counts.get(contentType) || 0) + 1);
1495
+ }
1496
+ }
1497
+ this.statistics.contentTypeDistribution = Array.from(counts.entries()).map(([contentType, count]) => ({
1498
+ contentType,
1499
+ count,
1500
+ percentage: totalRequests > 0 ? Math.round((count / totalRequests) * 100) : 0,
1501
+ }));
1502
+ this.contentTypeDistributionInitialized = true;
1503
+ yield this.saveStatistics();
1504
+ }
1505
+ finally {
1506
+ this.contentTypeDistributionInitializing = false;
1507
+ }
1508
+ });
1509
+ }
1201
1510
  // Statistics operations
1202
1511
  /**
1203
1512
  * 更新统计数据 - 在每次添加日志时调用
@@ -1297,6 +1606,19 @@ class FileSystemDatabaseManager {
1297
1606
  modelStats.avgResponseTime =
1298
1607
  (modelStats.avgResponseTime * (modelStats.totalRequests - 1) + responseTime) / modelStats.totalRequests;
1299
1608
  }
1609
+ // 更新 contentTypeDistribution
1610
+ const resolvedContentType = this.resolveLogContentType(log);
1611
+ let contentTypeStats = this.statistics.contentTypeDistribution.find(s => s.contentType === resolvedContentType);
1612
+ if (!contentTypeStats) {
1613
+ contentTypeStats = { contentType: resolvedContentType, count: 0, percentage: 0 };
1614
+ this.statistics.contentTypeDistribution.push(contentTypeStats);
1615
+ }
1616
+ contentTypeStats.count++;
1617
+ const totalRequests = this.statistics.overview.totalRequests;
1618
+ for (const entry of this.statistics.contentTypeDistribution) {
1619
+ entry.percentage = totalRequests > 0 ? Math.round((entry.count / totalRequests) * 100) : 0;
1620
+ }
1621
+ this.contentTypeDistributionInitialized = true;
1300
1622
  // 更新 timeline
1301
1623
  const date = new Date(log.timestamp).toISOString().split('T')[0];
1302
1624
  let timelineStats = this.statistics.timeline.find(t => t.date === date);
@@ -1324,6 +1646,7 @@ class FileSystemDatabaseManager {
1324
1646
  */
1325
1647
  getStatistics() {
1326
1648
  return __awaiter(this, arguments, void 0, function* (days = 30) {
1649
+ yield this.ensureContentTypeDistribution();
1327
1650
  const now = Date.now();
1328
1651
  const startTime = now - days * 24 * 60 * 60 * 1000;
1329
1652
  // 过滤 timeline 数据
@@ -1420,7 +1743,8 @@ class FileSystemDatabaseManager {
1420
1743
  // 检查 body 中的 metadata.user_id(Claude Code)
1421
1744
  if (log.body) {
1422
1745
  try {
1423
- const body = JSON.parse(log.body);
1746
+ // body 可能是对象(已解析)或字符串(未解析)
1747
+ const body = typeof log.body === 'string' ? JSON.parse(log.body) : log.body;
1424
1748
  if (((_b = body.metadata) === null || _b === void 0 ? void 0 : _b.user_id) === sessionId) {
1425
1749
  return true;
1426
1750
  }