aicodeswitch 1.10.1 → 1.10.2

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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### 1.10.2 (2026-01-26)
6
+
5
7
  ### 1.10.1 (2026-01-25)
6
8
 
7
9
  ## [1.10.0](https://github.com/tangshuang/aicodeswitch/compare/v1.9.0...v1.10.0) (2026-01-25)
@@ -180,6 +180,96 @@ class DatabaseManager {
180
180
  this.db.exec('ALTER TABLE api_services ADD COLUMN enable_proxy INTEGER DEFAULT 0;');
181
181
  console.log('[DB] Migration completed: enable_proxy column added');
182
182
  }
183
+ // 检查rules表是否有请求次数相关字段
184
+ const hasRequestCountLimit = rulesColumns.some((col) => col.name === 'request_count_limit');
185
+ const hasTotalRequestsUsed = rulesColumns.some((col) => col.name === 'total_requests_used');
186
+ const hasRequestResetInterval = rulesColumns.some((col) => col.name === 'request_reset_interval');
187
+ const hasRequestLastResetAt = rulesColumns.some((col) => col.name === 'request_last_reset_at');
188
+ if (!hasRequestCountLimit) {
189
+ console.log('[DB] Running migration: Adding request_count_limit column to rules table');
190
+ this.db.exec('ALTER TABLE rules ADD COLUMN request_count_limit INTEGER;');
191
+ console.log('[DB] Migration completed: request_count_limit column added');
192
+ }
193
+ if (!hasTotalRequestsUsed) {
194
+ console.log('[DB] Running migration: Adding total_requests_used column to rules table');
195
+ this.db.exec('ALTER TABLE rules ADD COLUMN total_requests_used INTEGER DEFAULT 0;');
196
+ console.log('[DB] Migration completed: total_requests_used column added');
197
+ }
198
+ if (!hasRequestResetInterval) {
199
+ console.log('[DB] Running migration: Adding request_reset_interval column to rules table');
200
+ this.db.exec('ALTER TABLE rules ADD COLUMN request_reset_interval INTEGER;');
201
+ console.log('[DB] Migration completed: request_reset_interval column added');
202
+ }
203
+ if (!hasRequestLastResetAt) {
204
+ console.log('[DB] Running migration: Adding request_last_reset_at column to rules table');
205
+ this.db.exec('ALTER TABLE rules ADD COLUMN request_last_reset_at INTEGER;');
206
+ console.log('[DB] Migration completed: request_last_reset_at column added');
207
+ }
208
+ // 检查rules表是否有request_reset_base_time字段
209
+ const hasRequestResetBaseTime = rulesColumns.some((col) => col.name === 'request_reset_base_time');
210
+ if (!hasRequestResetBaseTime) {
211
+ console.log('[DB] Running migration: Adding request_reset_base_time column to rules table');
212
+ this.db.exec('ALTER TABLE rules ADD COLUMN request_reset_base_time INTEGER;');
213
+ console.log('[DB] Migration completed: request_reset_base_time column added');
214
+ }
215
+ // 检查rules表是否有token_reset_base_time字段
216
+ const hasTokenResetBaseTime = rulesColumns.some((col) => col.name === 'token_reset_base_time');
217
+ if (!hasTokenResetBaseTime) {
218
+ console.log('[DB] Running migration: Adding token_reset_base_time column to rules table');
219
+ this.db.exec('ALTER TABLE rules ADD COLUMN token_reset_base_time INTEGER;');
220
+ console.log('[DB] Migration completed: token_reset_base_time column added');
221
+ }
222
+ // 检查api_services表是否有超量配置相关字段
223
+ // Token超量配置
224
+ const hasEnableTokenLimit = columns.some((col) => col.name === 'enable_token_limit');
225
+ if (!hasEnableTokenLimit) {
226
+ console.log('[DB] Running migration: Adding enable_token_limit column to api_services table');
227
+ this.db.exec('ALTER TABLE api_services ADD COLUMN enable_token_limit INTEGER DEFAULT 0;');
228
+ console.log('[DB] Migration completed: enable_token_limit column added');
229
+ }
230
+ const hasServiceTokenLimit = columns.some((col) => col.name === 'token_limit');
231
+ if (!hasServiceTokenLimit) {
232
+ console.log('[DB] Running migration: Adding token_limit column to api_services table');
233
+ this.db.exec('ALTER TABLE api_services ADD COLUMN token_limit INTEGER;');
234
+ console.log('[DB] Migration completed: token_limit column added');
235
+ }
236
+ const hasTokenResetInterval = columns.some((col) => col.name === 'token_reset_interval');
237
+ if (!hasTokenResetInterval) {
238
+ console.log('[DB] Running migration: Adding token_reset_interval column to api_services table');
239
+ this.db.exec('ALTER TABLE api_services ADD COLUMN token_reset_interval INTEGER;');
240
+ console.log('[DB] Migration completed: token_reset_interval column added');
241
+ }
242
+ const hasServiceTokenResetBaseTime = columns.some((col) => col.name === 'token_reset_base_time');
243
+ if (!hasServiceTokenResetBaseTime) {
244
+ console.log('[DB] Running migration: Adding token_reset_base_time column to api_services table');
245
+ this.db.exec('ALTER TABLE api_services ADD COLUMN token_reset_base_time INTEGER;');
246
+ console.log('[DB] Migration completed: token_reset_base_time column added');
247
+ }
248
+ // 请求次数超量配置
249
+ const hasEnableRequestLimit = columns.some((col) => col.name === 'enable_request_limit');
250
+ if (!hasEnableRequestLimit) {
251
+ console.log('[DB] Running migration: Adding enable_request_limit column to api_services table');
252
+ this.db.exec('ALTER TABLE api_services ADD COLUMN enable_request_limit INTEGER DEFAULT 0;');
253
+ console.log('[DB] Migration completed: enable_request_limit column added');
254
+ }
255
+ const hasServiceRequestCountLimit = columns.some((col) => col.name === 'request_count_limit');
256
+ if (!hasServiceRequestCountLimit) {
257
+ console.log('[DB] Running migration: Adding request_count_limit column to api_services table');
258
+ this.db.exec('ALTER TABLE api_services ADD COLUMN request_count_limit INTEGER;');
259
+ console.log('[DB] Migration completed: request_count_limit column added');
260
+ }
261
+ const hasServiceRequestResetInterval = columns.some((col) => col.name === 'request_reset_interval');
262
+ if (!hasServiceRequestResetInterval) {
263
+ console.log('[DB] Running migration: Adding request_reset_interval column to api_services table');
264
+ this.db.exec('ALTER TABLE api_services ADD COLUMN request_reset_interval INTEGER;');
265
+ console.log('[DB] Migration completed: request_reset_interval column added');
266
+ }
267
+ const hasServiceRequestResetBaseTime = columns.some((col) => col.name === 'request_reset_base_time');
268
+ if (!hasServiceRequestResetBaseTime) {
269
+ console.log('[DB] Running migration: Adding request_reset_base_time column to api_services table');
270
+ this.db.exec('ALTER TABLE api_services ADD COLUMN request_reset_base_time INTEGER;');
271
+ console.log('[DB] Migration completed: request_reset_base_time column added');
272
+ }
183
273
  });
184
274
  }
185
275
  migrateMaxOutputTokensToModelLimits() {
@@ -294,6 +384,10 @@ class DatabaseManager {
294
384
  total_tokens_used INTEGER DEFAULT 0,
295
385
  reset_interval INTEGER,
296
386
  last_reset_at INTEGER,
387
+ request_count_limit INTEGER,
388
+ total_requests_used INTEGER DEFAULT 0,
389
+ request_reset_interval INTEGER,
390
+ request_last_reset_at INTEGER,
297
391
  created_at INTEGER NOT NULL,
298
392
  updated_at INTEGER NOT NULL,
299
393
  FOREIGN KEY (route_id) REFERENCES routes(id) ON DELETE CASCADE,
@@ -375,6 +469,16 @@ class DatabaseManager {
375
469
  supportedModels: row.supported_models ? row.supported_models.split(',').map((model) => model.trim()).filter((model) => model.length > 0) : undefined,
376
470
  modelLimits: row.model_limits ? JSON.parse(row.model_limits) : undefined,
377
471
  enableProxy: row.enable_proxy === 1,
472
+ // Token超量配置
473
+ enableTokenLimit: row.enable_token_limit === 1,
474
+ tokenLimit: row.token_limit,
475
+ tokenResetInterval: row.token_reset_interval,
476
+ tokenResetBaseTime: row.token_reset_base_time,
477
+ // 请求次数超量配置
478
+ enableRequestLimit: row.enable_request_limit === 1,
479
+ requestCountLimit: row.request_count_limit,
480
+ requestResetInterval: row.request_reset_interval,
481
+ requestResetBaseTime: row.request_reset_base_time,
378
482
  createdAt: row.created_at,
379
483
  updatedAt: row.updated_at,
380
484
  }));
@@ -388,15 +492,15 @@ class DatabaseManager {
388
492
  const id = crypto_1.default.randomUUID();
389
493
  const now = Date.now();
390
494
  this.db
391
- .prepare('INSERT INTO api_services (id, vendor_id, name, api_url, api_key, source_type, supported_models, model_limits, enable_proxy, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
392
- .run(id, service.vendorId, service.name, service.apiUrl, service.apiKey, service.sourceType || null, service.supportedModels ? service.supportedModels.join(',') : null, service.modelLimits ? JSON.stringify(service.modelLimits) : null, service.enableProxy ? 1 : 0, now, now);
495
+ .prepare('INSERT INTO api_services (id, vendor_id, name, api_url, api_key, source_type, supported_models, model_limits, enable_proxy, enable_token_limit, token_limit, token_reset_interval, token_reset_base_time, enable_request_limit, request_count_limit, request_reset_interval, request_reset_base_time, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
496
+ .run(id, service.vendorId, service.name, service.apiUrl, service.apiKey, service.sourceType || null, service.supportedModels ? service.supportedModels.join(',') : null, service.modelLimits ? JSON.stringify(service.modelLimits) : null, service.enableProxy ? 1 : 0, service.enableTokenLimit ? 1 : 0, service.tokenLimit || null, service.tokenResetInterval || null, service.tokenResetBaseTime || null, service.enableRequestLimit ? 1 : 0, service.requestCountLimit || null, service.requestResetInterval || null, service.requestResetBaseTime || null, now, now);
393
497
  return Object.assign(Object.assign({}, service), { id, createdAt: now, updatedAt: now });
394
498
  }
395
499
  updateAPIService(id, service) {
396
500
  const now = Date.now();
397
501
  const result = this.db
398
- .prepare('UPDATE api_services SET name = ?, api_url = ?, api_key = ?, source_type = ?, supported_models = ?, model_limits = ?, enable_proxy = ?, updated_at = ? WHERE id = ?')
399
- .run(service.name, service.apiUrl, service.apiKey, service.sourceType || null, service.supportedModels ? service.supportedModels.join(',') : null, service.modelLimits ? JSON.stringify(service.modelLimits) : null, service.enableProxy !== undefined ? (service.enableProxy ? 1 : 0) : null, now, id);
502
+ .prepare('UPDATE api_services SET vendor_id = ?, name = ?, api_url = ?, api_key = ?, source_type = ?, supported_models = ?, model_limits = ?, enable_proxy = ?, enable_token_limit = ?, token_limit = ?, token_reset_interval = ?, token_reset_base_time = ?, enable_request_limit = ?, request_count_limit = ?, request_reset_interval = ?, request_reset_base_time = ?, updated_at = ? WHERE id = ?')
503
+ .run(service.vendorId, service.name, service.apiUrl, service.apiKey, service.sourceType || null, service.supportedModels ? service.supportedModels.join(',') : null, service.modelLimits ? JSON.stringify(service.modelLimits) : null, service.enableProxy !== undefined ? (service.enableProxy ? 1 : 0) : null, service.enableTokenLimit !== undefined ? (service.enableTokenLimit ? 1 : 0) : null, service.tokenLimit !== undefined ? service.tokenLimit : null, service.tokenResetInterval !== undefined ? service.tokenResetInterval : null, service.tokenResetBaseTime !== undefined ? service.tokenResetBaseTime : null, service.enableRequestLimit !== undefined ? (service.enableRequestLimit ? 1 : 0) : null, service.requestCountLimit !== undefined ? service.requestCountLimit : null, service.requestResetInterval !== undefined ? service.requestResetInterval : null, service.requestResetBaseTime !== undefined ? service.requestResetBaseTime : null, now, id);
400
504
  // 调试日志: 记录更新操作
401
505
  if (result.changes > 0 && process.env.NODE_ENV === 'development') {
402
506
  console.log(`[DB] Updated service ${id}: ${service.name} -> ${service.apiUrl}`);
@@ -472,23 +576,56 @@ class DatabaseManager {
472
576
  totalTokensUsed: row.total_tokens_used,
473
577
  resetInterval: row.reset_interval,
474
578
  lastResetAt: row.last_reset_at,
579
+ tokenResetBaseTime: row.token_reset_base_time,
580
+ requestCountLimit: row.request_count_limit,
581
+ totalRequestsUsed: row.total_requests_used,
582
+ requestResetInterval: row.request_reset_interval,
583
+ requestLastResetAt: row.request_last_reset_at,
584
+ requestResetBaseTime: row.request_reset_base_time,
475
585
  createdAt: row.created_at,
476
586
  updatedAt: row.updated_at,
477
587
  }));
478
588
  }
589
+ getRule(id) {
590
+ const row = this.db.prepare('SELECT * FROM rules WHERE id = ?').get(id);
591
+ if (!row)
592
+ return undefined;
593
+ return {
594
+ id: row.id,
595
+ routeId: row.route_id,
596
+ contentType: row.content_type,
597
+ targetServiceId: row.target_service_id,
598
+ targetModel: row.target_model,
599
+ replacedModel: row.replaced_model,
600
+ sortOrder: row.sort_order,
601
+ timeout: row.timeout,
602
+ tokenLimit: row.token_limit,
603
+ totalTokensUsed: row.total_tokens_used,
604
+ resetInterval: row.reset_interval,
605
+ lastResetAt: row.last_reset_at,
606
+ tokenResetBaseTime: row.token_reset_base_time,
607
+ requestCountLimit: row.request_count_limit,
608
+ totalRequestsUsed: row.total_requests_used,
609
+ requestResetInterval: row.request_reset_interval,
610
+ requestLastResetAt: row.request_last_reset_at,
611
+ requestResetBaseTime: row.request_reset_base_time,
612
+ createdAt: row.created_at,
613
+ updatedAt: row.updated_at,
614
+ };
615
+ }
479
616
  createRule(route) {
480
617
  const id = crypto_1.default.randomUUID();
481
618
  const now = Date.now();
482
619
  this.db
483
- .prepare('INSERT INTO rules (id, route_id, content_type, target_service_id, target_model, replaced_model, sort_order, timeout, token_limit, total_tokens_used, reset_interval, last_reset_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
484
- .run(id, route.routeId, route.contentType, route.targetServiceId, route.targetModel || null, route.replacedModel || null, route.sortOrder || 0, route.timeout || null, route.tokenLimit || null, route.totalTokensUsed || 0, route.resetInterval || null, route.lastResetAt || null, now, now);
620
+ .prepare('INSERT INTO rules (id, route_id, content_type, target_service_id, target_model, replaced_model, sort_order, timeout, token_limit, total_tokens_used, reset_interval, last_reset_at, token_reset_base_time, request_count_limit, total_requests_used, request_reset_interval, request_last_reset_at, request_reset_base_time, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
621
+ .run(id, route.routeId, route.contentType, route.targetServiceId, route.targetModel || null, route.replacedModel || null, route.sortOrder || 0, route.timeout || null, route.tokenLimit || null, route.totalTokensUsed || 0, route.resetInterval || null, route.lastResetAt || null, route.tokenResetBaseTime || null, route.requestCountLimit || null, route.totalRequestsUsed || 0, route.requestResetInterval || null, route.requestLastResetAt || null, route.requestResetBaseTime || null, now, now);
485
622
  return Object.assign(Object.assign({}, route), { id, createdAt: now, updatedAt: now });
486
623
  }
487
624
  updateRule(id, route) {
488
625
  const now = Date.now();
489
626
  const result = this.db
490
- .prepare('UPDATE rules SET content_type = ?, target_service_id = ?, target_model = ?, replaced_model = ?, sort_order = ?, timeout = ?, token_limit = ?, reset_interval = ?, updated_at = ? WHERE id = ?')
491
- .run(route.contentType, route.targetServiceId, route.targetModel || null, route.replacedModel || null, route.sortOrder || 0, route.timeout !== undefined ? route.timeout : null, route.tokenLimit !== undefined ? route.tokenLimit : null, route.resetInterval !== undefined ? route.resetInterval : null, now, id);
627
+ .prepare('UPDATE rules SET content_type = ?, target_service_id = ?, target_model = ?, replaced_model = ?, sort_order = ?, timeout = ?, token_limit = ?, reset_interval = ?, token_reset_base_time = ?, request_count_limit = ?, request_reset_interval = ?, request_reset_base_time = ?, updated_at = ? WHERE id = ?')
628
+ .run(route.contentType, route.targetServiceId, route.targetModel || null, route.replacedModel || null, route.sortOrder || 0, route.timeout !== undefined ? route.timeout : null, route.tokenLimit !== undefined ? route.tokenLimit : null, route.resetInterval !== undefined ? route.resetInterval : null, route.tokenResetBaseTime !== undefined ? route.tokenResetBaseTime : null, route.requestCountLimit !== undefined ? route.requestCountLimit : null, route.requestResetInterval !== undefined ? route.requestResetInterval : null, route.requestResetBaseTime !== undefined ? route.requestResetBaseTime : null, now, id);
492
629
  return result.changes > 0;
493
630
  }
494
631
  deleteRule(id) {
@@ -527,28 +664,133 @@ class DatabaseManager {
527
664
  */
528
665
  checkAndResetRuleIfNeeded(ruleId) {
529
666
  const rule = this.db
530
- .prepare('SELECT reset_interval, last_reset_at FROM rules WHERE id = ?')
667
+ .prepare('SELECT reset_interval, last_reset_at, token_reset_base_time FROM rules WHERE id = ?')
531
668
  .get(ruleId);
532
669
  if (!rule || !rule.reset_interval) {
533
670
  return false; // 没有设置重置间隔
534
671
  }
535
672
  const now = Date.now();
536
673
  const resetIntervalMs = rule.reset_interval * 60 * 60 * 1000; // 小时转毫秒
674
+ const baseTime = rule.token_reset_base_time;
537
675
  const lastResetAt = rule.last_reset_at || 0;
538
- // 检查是否已经到了重置时间
676
+ // 场景1: 设置了时间基点
677
+ if (baseTime) {
678
+ if (now >= baseTime) {
679
+ this.resetRuleTokenUsageWithBaseTime(ruleId, baseTime);
680
+ return true;
681
+ }
682
+ return false;
683
+ }
684
+ // 场景2: 未设置时间基点,使用原始逻辑(向后兼容)
539
685
  if (now - lastResetAt >= resetIntervalMs) {
540
686
  this.resetRuleTokenUsage(ruleId);
541
687
  return true;
542
688
  }
543
689
  return false;
544
690
  }
691
+ /**
692
+ * 重置规则的Token使用量(带时间基点更新)
693
+ */
694
+ resetRuleTokenUsageWithBaseTime(ruleId, currentBaseTime) {
695
+ const now = Date.now();
696
+ const rule = this.db
697
+ .prepare('SELECT reset_interval FROM rules WHERE id = ?')
698
+ .get(ruleId);
699
+ if (!rule || !rule.reset_interval) {
700
+ return false;
701
+ }
702
+ const resetIntervalMs = rule.reset_interval * 60 * 60 * 1000;
703
+ // 计算下一个时间基点
704
+ let nextBaseTime = currentBaseTime;
705
+ while (nextBaseTime <= now) {
706
+ nextBaseTime += resetIntervalMs;
707
+ }
708
+ const result = this.db
709
+ .prepare('UPDATE rules SET total_tokens_used = 0, last_reset_at = ?, token_reset_base_time = ? WHERE id = ?')
710
+ .run(now, nextBaseTime, ruleId);
711
+ return result.changes > 0;
712
+ }
713
+ /**
714
+ * 增加规则的请求次数
715
+ * @param ruleId 规则ID
716
+ * @param count 增加的次数
717
+ * @returns 是否成功
718
+ */
719
+ incrementRuleRequestCount(ruleId, count) {
720
+ const result = this.db
721
+ .prepare('UPDATE rules SET total_requests_used = total_requests_used + ? WHERE id = ?')
722
+ .run(count, ruleId);
723
+ return result.changes > 0;
724
+ }
725
+ /**
726
+ * 重置规则的请求次数
727
+ * @param ruleId 规则ID
728
+ * @returns 是否成功
729
+ */
730
+ resetRuleRequestCount(ruleId) {
731
+ const now = Date.now();
732
+ const result = this.db
733
+ .prepare('UPDATE rules SET total_requests_used = 0, request_last_reset_at = ? WHERE id = ?')
734
+ .run(now, ruleId);
735
+ return result.changes > 0;
736
+ }
737
+ /**
738
+ * 检查并重置到期的规则(请求次数)
739
+ * 如果规则设置了request_reset_interval且已经到了重置时间,则自动重置请求次数
740
+ * @param ruleId 规则ID
741
+ * @returns 是否进行了重置
742
+ */
743
+ checkAndResetRequestCountIfNeeded(ruleId) {
744
+ const rule = this.db
745
+ .prepare('SELECT request_reset_interval, request_last_reset_at, request_reset_base_time FROM rules WHERE id = ?')
746
+ .get(ruleId);
747
+ if (!rule || !rule.request_reset_interval) {
748
+ return false; // 没有设置重置间隔
749
+ }
750
+ const now = Date.now();
751
+ const resetIntervalMs = rule.request_reset_interval * 60 * 60 * 1000; // 小时转毫秒
752
+ const baseTime = rule.request_reset_base_time;
753
+ const lastResetAt = rule.request_last_reset_at || 0;
754
+ // 场景1: 设置了时间基点
755
+ if (baseTime) {
756
+ if (now >= baseTime) {
757
+ this.resetRuleRequestCountWithBaseTime(ruleId, baseTime);
758
+ return true;
759
+ }
760
+ return false;
761
+ }
762
+ // 场景2: 未设置时间基点,使用原始逻辑(向后兼容)
763
+ if (now - lastResetAt >= resetIntervalMs) {
764
+ this.resetRuleRequestCount(ruleId);
765
+ return true;
766
+ }
767
+ return false;
768
+ }
769
+ /**
770
+ * 重置规则的请求次数(带时间基点更新)
771
+ */
772
+ resetRuleRequestCountWithBaseTime(ruleId, currentBaseTime) {
773
+ const now = Date.now();
774
+ const rule = this.db
775
+ .prepare('SELECT request_reset_interval FROM rules WHERE id = ?')
776
+ .get(ruleId);
777
+ if (!rule || !rule.request_reset_interval) {
778
+ return false;
779
+ }
780
+ const resetIntervalMs = rule.request_reset_interval * 60 * 60 * 1000;
781
+ // 计算下一个时间基点
782
+ let nextBaseTime = currentBaseTime;
783
+ while (nextBaseTime <= now) {
784
+ nextBaseTime += resetIntervalMs;
785
+ }
786
+ const result = this.db
787
+ .prepare('UPDATE rules SET total_requests_used = 0, request_last_reset_at = ?, request_reset_base_time = ? WHERE id = ?')
788
+ .run(now, nextBaseTime, ruleId);
789
+ return result.changes > 0;
790
+ }
545
791
  // Log operations
546
792
  addLog(log) {
547
793
  return __awaiter(this, void 0, void 0, function* () {
548
- const { path } = log;
549
- if (!path.startsWith('/v1/')) {
550
- return;
551
- }
552
794
  const id = crypto_1.default.randomUUID();
553
795
  yield this.logDb.put(id, JSON.stringify(Object.assign(Object.assign({}, log), { id })));
554
796
  // 清除缓存
@@ -790,7 +1032,7 @@ class DatabaseManager {
790
1032
  }
791
1033
  });
792
1034
  }
793
- addToBlacklist(serviceId, routeId, contentType, errorMessage, statusCode) {
1035
+ addToBlacklist(serviceId, routeId, contentType, errorMessage, statusCode, errorType) {
794
1036
  return __awaiter(this, void 0, void 0, function* () {
795
1037
  const key = `${routeId}:${contentType}:${serviceId}`;
796
1038
  const now = Date.now();
@@ -804,6 +1046,7 @@ class DatabaseManager {
804
1046
  entry.errorCount++;
805
1047
  entry.lastError = errorMessage;
806
1048
  entry.lastStatusCode = statusCode;
1049
+ entry.errorType = errorType;
807
1050
  yield this.blacklistDb.put(key, JSON.stringify(entry));
808
1051
  }
809
1052
  catch (error) {
@@ -818,6 +1061,7 @@ class DatabaseManager {
818
1061
  errorCount: 1,
819
1062
  lastError: errorMessage,
820
1063
  lastStatusCode: statusCode,
1064
+ errorType,
821
1065
  };
822
1066
  yield this.blacklistDb.put(key, JSON.stringify(entry));
823
1067
  }
@@ -827,6 +1071,12 @@ class DatabaseManager {
827
1071
  }
828
1072
  });
829
1073
  }
1074
+ removeFromBlacklist(serviceId, routeId, contentType) {
1075
+ return __awaiter(this, void 0, void 0, function* () {
1076
+ const key = `${routeId}:${contentType}:${serviceId}`;
1077
+ yield this.blacklistDb.del(key);
1078
+ });
1079
+ }
830
1080
  cleanupExpiredBlacklist() {
831
1081
  return __awaiter(this, void 0, void 0, function* () {
832
1082
  var _a, e_7, _b, _c;
@@ -902,8 +1152,8 @@ class DatabaseManager {
902
1152
  // Import API services
903
1153
  for (const service of importData.apiServices) {
904
1154
  this.db
905
- .prepare('INSERT INTO api_services (id, vendor_id, name, api_url, api_key, source_type, supported_models, model_limits, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
906
- .run(service.id, service.vendorId, service.name, service.apiUrl, service.apiKey, service.sourceType || null, service.supportedModels ? service.supportedModels.join(',') : null, service.modelLimits ? JSON.stringify(service.modelLimits) : null, service.createdAt, service.updatedAt);
1155
+ .prepare('INSERT INTO api_services (id, vendor_id, name, api_url, api_key, source_type, supported_models, model_limits, enable_proxy, enable_token_limit, token_limit, token_reset_interval, token_reset_base_time, enable_request_limit, request_count_limit, request_reset_interval, request_reset_base_time, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
1156
+ .run(service.id, service.vendorId, service.name, service.apiUrl, service.apiKey, service.sourceType || null, service.supportedModels ? service.supportedModels.join(',') : null, service.modelLimits ? JSON.stringify(service.modelLimits) : null, service.enableProxy ? 1 : 0, service.enableTokenLimit ? 1 : 0, service.tokenLimit || null, service.tokenResetInterval || null, service.tokenResetBaseTime || null, service.enableRequestLimit ? 1 : 0, service.requestCountLimit || null, service.requestResetInterval || null, service.requestResetBaseTime || null, service.createdAt, service.updatedAt);
907
1157
  }
908
1158
  // Import routes
909
1159
  for (const route of importData.routes) {
@@ -914,8 +1164,8 @@ class DatabaseManager {
914
1164
  // Import rules
915
1165
  for (const rule of importData.rules) {
916
1166
  this.db
917
- .prepare('INSERT INTO rules (id, route_id, content_type, target_service_id, target_model, replaced_model, sort_order, timeout, token_limit, total_tokens_used, reset_interval, last_reset_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
918
- .run(rule.id, rule.routeId, rule.contentType || 'default', rule.targetServiceId, rule.targetModel || null, rule.replacedModel || null, rule.sortOrder || 0, rule.timeout || null, rule.tokenLimit || null, rule.totalTokensUsed || 0, rule.resetInterval || null, rule.lastResetAt || null, rule.createdAt, rule.updatedAt);
1167
+ .prepare('INSERT INTO rules (id, route_id, content_type, target_service_id, target_model, replaced_model, sort_order, timeout, token_limit, total_tokens_used, reset_interval, last_reset_at, token_reset_base_time, request_count_limit, total_requests_used, request_reset_interval, request_last_reset_at, request_reset_base_time, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
1168
+ .run(rule.id, rule.routeId, rule.contentType || 'default', rule.targetServiceId, rule.targetModel || null, rule.replacedModel || null, rule.sortOrder || 0, rule.timeout || null, rule.tokenLimit || null, rule.totalTokensUsed || 0, rule.resetInterval || null, rule.lastResetAt || null, rule.tokenResetBaseTime || null, rule.requestCountLimit || null, rule.totalRequestsUsed || 0, rule.requestResetInterval || null, rule.requestLastResetAt || null, rule.requestResetBaseTime || null, rule.createdAt, rule.updatedAt);
919
1169
  }
920
1170
  // Update config
921
1171
  this.updateConfig(importData.config);
@@ -1150,6 +1400,27 @@ class DatabaseManager {
1150
1400
  };
1151
1401
  });
1152
1402
  }
1403
+ getRuleBlacklistStatus(serviceId, routeId, contentType) {
1404
+ return __awaiter(this, void 0, void 0, function* () {
1405
+ const key = `${routeId}:${contentType}:${serviceId}`;
1406
+ try {
1407
+ const value = yield this.blacklistDb.get(key);
1408
+ const entry = JSON.parse(value);
1409
+ // 检查是否过期
1410
+ if (Date.now() > entry.expiresAt) {
1411
+ yield this.blacklistDb.del(key);
1412
+ return null;
1413
+ }
1414
+ return entry;
1415
+ }
1416
+ catch (error) {
1417
+ if (error.code === 'LEVEL_NOT_FOUND') {
1418
+ return null;
1419
+ }
1420
+ throw error;
1421
+ }
1422
+ });
1423
+ }
1153
1424
  close() {
1154
1425
  this.db.close();
1155
1426
  this.logDb.close();
@@ -283,6 +283,54 @@ const registerRoutes = (dbManager, proxyServer) => {
283
283
  app.put('/api/rules/:id', (req, res) => res.json(dbManager.updateRule(req.params.id, req.body)));
284
284
  app.delete('/api/rules/:id', (req, res) => res.json(dbManager.deleteRule(req.params.id)));
285
285
  app.put('/api/rules/:id/reset-tokens', (req, res) => res.json(dbManager.resetRuleTokenUsage(req.params.id)));
286
+ app.put('/api/rules/:id/reset-requests', (req, res) => res.json(dbManager.resetRuleRequestCount(req.params.id)));
287
+ // 解除规则的黑名单状态
288
+ app.put('/api/rules/:id/clear-blacklist', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
289
+ const { id } = req.params;
290
+ const rule = dbManager.getRule(id);
291
+ if (!rule) {
292
+ res.status(404).json({ error: 'Rule not found' });
293
+ return;
294
+ }
295
+ // 找到该规则所属的路由
296
+ const routes = dbManager.getRoutes();
297
+ const route = routes.find(r => {
298
+ const rules = dbManager.getRules(r.id);
299
+ return rules.some(r => r.id === id);
300
+ });
301
+ if (!route) {
302
+ res.status(404).json({ error: 'Route not found' });
303
+ return;
304
+ }
305
+ try {
306
+ yield dbManager.removeFromBlacklist(rule.targetServiceId, route.id, rule.contentType);
307
+ res.json({ success: true });
308
+ }
309
+ catch (error) {
310
+ console.error('Error clearing blacklist:', error);
311
+ res.status(500).json({ error: 'Failed to clear blacklist' });
312
+ }
313
+ })));
314
+ // 获取规则的黑名单状态
315
+ app.get('/api/rules/:routeId/blacklist-status', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
316
+ const { routeId } = req.params;
317
+ const rules = dbManager.getRules(routeId);
318
+ try {
319
+ const results = yield Promise.all(rules.map((rule) => __awaiter(void 0, void 0, void 0, function* () {
320
+ const blacklistStatus = yield dbManager.getRuleBlacklistStatus(rule.targetServiceId, routeId, rule.contentType);
321
+ return {
322
+ ruleId: rule.id,
323
+ isBlacklisted: blacklistStatus !== null,
324
+ blacklistEntry: blacklistStatus,
325
+ };
326
+ })));
327
+ res.json(results);
328
+ }
329
+ catch (error) {
330
+ console.error('Error getting blacklist status:', error);
331
+ res.status(500).json({ error: 'Failed to get blacklist status' });
332
+ }
333
+ })));
286
334
  app.get('/api/logs', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
287
335
  const rawLimit = typeof req.query.limit === 'string' ? parseInt(req.query.limit, 10) : NaN;
288
336
  const rawOffset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : NaN;