aicodeswitch 1.10.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/bin/cli.js +7 -9
- package/bin/restart.js +7 -229
- package/bin/restore.js +1 -1
- package/bin/start.js +75 -87
- package/bin/stop.js +77 -14
- package/bin/ui.js +19 -134
- package/bin/update.js +1 -1
- package/bin/utils/get-server.js +58 -0
- package/bin/utils/port-utils.js +118 -0
- package/bin/version.js +1 -1
- package/dist/server/database.js +480 -129
- package/dist/server/main.js +164 -22
- package/dist/server/proxy-server.js +417 -120
- package/dist/server/transformers/claude-openai.js +86 -3
- package/dist/server/transformers/streaming.js +4 -1
- package/dist/server/utils.js +16 -0
- package/dist/ui/assets/index-BLqGemLn.js +423 -0
- package/dist/ui/assets/index-IVPeH7yC.css +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/ui/migration.md +7 -0
- package/package.json +3 -2
- package/public/migration.md +7 -0
- package/dist/ui/assets/index-BdKga7KO.js +0 -391
- package/dist/ui/assets/index-Dat4drax.css +0 -1
package/dist/server/database.js
CHANGED
|
@@ -39,12 +39,6 @@ class DatabaseManager {
|
|
|
39
39
|
writable: true,
|
|
40
40
|
value: void 0
|
|
41
41
|
});
|
|
42
|
-
Object.defineProperty(this, "accessLogDb", {
|
|
43
|
-
enumerable: true,
|
|
44
|
-
configurable: true,
|
|
45
|
-
writable: true,
|
|
46
|
-
value: void 0
|
|
47
|
-
});
|
|
48
42
|
Object.defineProperty(this, "errorLogDb", {
|
|
49
43
|
enumerable: true,
|
|
50
44
|
configurable: true,
|
|
@@ -64,12 +58,6 @@ class DatabaseManager {
|
|
|
64
58
|
writable: true,
|
|
65
59
|
value: null
|
|
66
60
|
});
|
|
67
|
-
Object.defineProperty(this, "accessLogsCountCache", {
|
|
68
|
-
enumerable: true,
|
|
69
|
-
configurable: true,
|
|
70
|
-
writable: true,
|
|
71
|
-
value: null
|
|
72
|
-
});
|
|
73
61
|
Object.defineProperty(this, "errorLogsCountCache", {
|
|
74
62
|
enumerable: true,
|
|
75
63
|
configurable: true,
|
|
@@ -94,7 +82,6 @@ class DatabaseManager {
|
|
|
94
82
|
// 设置 read_uncommitted = 0 确保读取最新提交的数据
|
|
95
83
|
this.db.pragma('read_uncommitted = 0');
|
|
96
84
|
this.logDb = new level_1.Level(path_1.default.join(dataPath, 'logs'), { valueEncoding: 'json' });
|
|
97
|
-
this.accessLogDb = new level_1.Level(path_1.default.join(dataPath, 'access-logs'), { valueEncoding: 'json' });
|
|
98
85
|
this.errorLogDb = new level_1.Level(path_1.default.join(dataPath, 'error-logs'), { valueEncoding: 'json' });
|
|
99
86
|
this.blacklistDb = new level_1.Level(path_1.default.join(dataPath, 'service-blacklist'), { valueEncoding: 'json' });
|
|
100
87
|
}
|
|
@@ -180,6 +167,103 @@ class DatabaseManager {
|
|
|
180
167
|
this.db.exec('ALTER TABLE api_services ADD COLUMN enable_proxy INTEGER DEFAULT 0;');
|
|
181
168
|
console.log('[DB] Migration completed: enable_proxy column added');
|
|
182
169
|
}
|
|
170
|
+
// 检查rules表是否有请求次数相关字段
|
|
171
|
+
const hasRequestCountLimit = rulesColumns.some((col) => col.name === 'request_count_limit');
|
|
172
|
+
const hasTotalRequestsUsed = rulesColumns.some((col) => col.name === 'total_requests_used');
|
|
173
|
+
const hasRequestResetInterval = rulesColumns.some((col) => col.name === 'request_reset_interval');
|
|
174
|
+
const hasRequestLastResetAt = rulesColumns.some((col) => col.name === 'request_last_reset_at');
|
|
175
|
+
if (!hasRequestCountLimit) {
|
|
176
|
+
console.log('[DB] Running migration: Adding request_count_limit column to rules table');
|
|
177
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN request_count_limit INTEGER;');
|
|
178
|
+
console.log('[DB] Migration completed: request_count_limit column added');
|
|
179
|
+
}
|
|
180
|
+
if (!hasTotalRequestsUsed) {
|
|
181
|
+
console.log('[DB] Running migration: Adding total_requests_used column to rules table');
|
|
182
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN total_requests_used INTEGER DEFAULT 0;');
|
|
183
|
+
console.log('[DB] Migration completed: total_requests_used column added');
|
|
184
|
+
}
|
|
185
|
+
if (!hasRequestResetInterval) {
|
|
186
|
+
console.log('[DB] Running migration: Adding request_reset_interval column to rules table');
|
|
187
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN request_reset_interval INTEGER;');
|
|
188
|
+
console.log('[DB] Migration completed: request_reset_interval column added');
|
|
189
|
+
}
|
|
190
|
+
if (!hasRequestLastResetAt) {
|
|
191
|
+
console.log('[DB] Running migration: Adding request_last_reset_at column to rules table');
|
|
192
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN request_last_reset_at INTEGER;');
|
|
193
|
+
console.log('[DB] Migration completed: request_last_reset_at column added');
|
|
194
|
+
}
|
|
195
|
+
// 检查rules表是否有request_reset_base_time字段
|
|
196
|
+
const hasRequestResetBaseTime = rulesColumns.some((col) => col.name === 'request_reset_base_time');
|
|
197
|
+
if (!hasRequestResetBaseTime) {
|
|
198
|
+
console.log('[DB] Running migration: Adding request_reset_base_time column to rules table');
|
|
199
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN request_reset_base_time INTEGER;');
|
|
200
|
+
console.log('[DB] Migration completed: request_reset_base_time column added');
|
|
201
|
+
}
|
|
202
|
+
// 检查rules表是否有token_reset_base_time字段
|
|
203
|
+
const hasTokenResetBaseTime = rulesColumns.some((col) => col.name === 'token_reset_base_time');
|
|
204
|
+
if (!hasTokenResetBaseTime) {
|
|
205
|
+
console.log('[DB] Running migration: Adding token_reset_base_time column to rules table');
|
|
206
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN token_reset_base_time INTEGER;');
|
|
207
|
+
console.log('[DB] Migration completed: token_reset_base_time column added');
|
|
208
|
+
}
|
|
209
|
+
// 检查api_services表是否有超量配置相关字段
|
|
210
|
+
// Token超量配置
|
|
211
|
+
const hasEnableTokenLimit = columns.some((col) => col.name === 'enable_token_limit');
|
|
212
|
+
if (!hasEnableTokenLimit) {
|
|
213
|
+
console.log('[DB] Running migration: Adding enable_token_limit column to api_services table');
|
|
214
|
+
this.db.exec('ALTER TABLE api_services ADD COLUMN enable_token_limit INTEGER DEFAULT 0;');
|
|
215
|
+
console.log('[DB] Migration completed: enable_token_limit column added');
|
|
216
|
+
}
|
|
217
|
+
const hasServiceTokenLimit = columns.some((col) => col.name === 'token_limit');
|
|
218
|
+
if (!hasServiceTokenLimit) {
|
|
219
|
+
console.log('[DB] Running migration: Adding token_limit column to api_services table');
|
|
220
|
+
this.db.exec('ALTER TABLE api_services ADD COLUMN token_limit INTEGER;');
|
|
221
|
+
console.log('[DB] Migration completed: token_limit column added');
|
|
222
|
+
}
|
|
223
|
+
const hasTokenResetInterval = columns.some((col) => col.name === 'token_reset_interval');
|
|
224
|
+
if (!hasTokenResetInterval) {
|
|
225
|
+
console.log('[DB] Running migration: Adding token_reset_interval column to api_services table');
|
|
226
|
+
this.db.exec('ALTER TABLE api_services ADD COLUMN token_reset_interval INTEGER;');
|
|
227
|
+
console.log('[DB] Migration completed: token_reset_interval column added');
|
|
228
|
+
}
|
|
229
|
+
const hasServiceTokenResetBaseTime = columns.some((col) => col.name === 'token_reset_base_time');
|
|
230
|
+
if (!hasServiceTokenResetBaseTime) {
|
|
231
|
+
console.log('[DB] Running migration: Adding token_reset_base_time column to api_services table');
|
|
232
|
+
this.db.exec('ALTER TABLE api_services ADD COLUMN token_reset_base_time INTEGER;');
|
|
233
|
+
console.log('[DB] Migration completed: token_reset_base_time column added');
|
|
234
|
+
}
|
|
235
|
+
// 请求次数超量配置
|
|
236
|
+
const hasEnableRequestLimit = columns.some((col) => col.name === 'enable_request_limit');
|
|
237
|
+
if (!hasEnableRequestLimit) {
|
|
238
|
+
console.log('[DB] Running migration: Adding enable_request_limit column to api_services table');
|
|
239
|
+
this.db.exec('ALTER TABLE api_services ADD COLUMN enable_request_limit INTEGER DEFAULT 0;');
|
|
240
|
+
console.log('[DB] Migration completed: enable_request_limit column added');
|
|
241
|
+
}
|
|
242
|
+
const hasServiceRequestCountLimit = columns.some((col) => col.name === 'request_count_limit');
|
|
243
|
+
if (!hasServiceRequestCountLimit) {
|
|
244
|
+
console.log('[DB] Running migration: Adding request_count_limit column to api_services table');
|
|
245
|
+
this.db.exec('ALTER TABLE api_services ADD COLUMN request_count_limit INTEGER;');
|
|
246
|
+
console.log('[DB] Migration completed: request_count_limit column added');
|
|
247
|
+
}
|
|
248
|
+
const hasServiceRequestResetInterval = columns.some((col) => col.name === 'request_reset_interval');
|
|
249
|
+
if (!hasServiceRequestResetInterval) {
|
|
250
|
+
console.log('[DB] Running migration: Adding request_reset_interval column to api_services table');
|
|
251
|
+
this.db.exec('ALTER TABLE api_services ADD COLUMN request_reset_interval INTEGER;');
|
|
252
|
+
console.log('[DB] Migration completed: request_reset_interval column added');
|
|
253
|
+
}
|
|
254
|
+
const hasServiceRequestResetBaseTime = columns.some((col) => col.name === 'request_reset_base_time');
|
|
255
|
+
if (!hasServiceRequestResetBaseTime) {
|
|
256
|
+
console.log('[DB] Running migration: Adding request_reset_base_time column to api_services table');
|
|
257
|
+
this.db.exec('ALTER TABLE api_services ADD COLUMN request_reset_base_time INTEGER;');
|
|
258
|
+
console.log('[DB] Migration completed: request_reset_base_time column added');
|
|
259
|
+
}
|
|
260
|
+
// 检查api_services表是否有auth_type字段
|
|
261
|
+
const hasAuthType = columns.some((col) => col.name === 'auth_type');
|
|
262
|
+
if (!hasAuthType) {
|
|
263
|
+
console.log('[DB] Running migration: Adding auth_type column to api_services table');
|
|
264
|
+
this.db.exec('ALTER TABLE api_services ADD COLUMN auth_type TEXT DEFAULT NULL;');
|
|
265
|
+
console.log('[DB] Migration completed: auth_type column added');
|
|
266
|
+
}
|
|
183
267
|
});
|
|
184
268
|
}
|
|
185
269
|
migrateMaxOutputTokensToModelLimits() {
|
|
@@ -294,6 +378,10 @@ class DatabaseManager {
|
|
|
294
378
|
total_tokens_used INTEGER DEFAULT 0,
|
|
295
379
|
reset_interval INTEGER,
|
|
296
380
|
last_reset_at INTEGER,
|
|
381
|
+
request_count_limit INTEGER,
|
|
382
|
+
total_requests_used INTEGER DEFAULT 0,
|
|
383
|
+
request_reset_interval INTEGER,
|
|
384
|
+
request_last_reset_at INTEGER,
|
|
297
385
|
created_at INTEGER NOT NULL,
|
|
298
386
|
updated_at INTEGER NOT NULL,
|
|
299
387
|
FOREIGN KEY (route_id) REFERENCES routes(id) ON DELETE CASCADE,
|
|
@@ -304,6 +392,21 @@ class DatabaseManager {
|
|
|
304
392
|
key TEXT PRIMARY KEY,
|
|
305
393
|
value TEXT NOT NULL
|
|
306
394
|
);
|
|
395
|
+
|
|
396
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
397
|
+
id TEXT PRIMARY KEY,
|
|
398
|
+
target_type TEXT NOT NULL CHECK(target_type IN ('claude-code', 'codex')),
|
|
399
|
+
title TEXT,
|
|
400
|
+
first_request_at INTEGER NOT NULL,
|
|
401
|
+
last_request_at INTEGER NOT NULL,
|
|
402
|
+
request_count INTEGER DEFAULT 1,
|
|
403
|
+
total_tokens INTEGER DEFAULT 0,
|
|
404
|
+
vendor_id TEXT,
|
|
405
|
+
vendor_name TEXT,
|
|
406
|
+
service_id TEXT,
|
|
407
|
+
service_name TEXT,
|
|
408
|
+
model TEXT
|
|
409
|
+
);
|
|
307
410
|
`);
|
|
308
411
|
}
|
|
309
412
|
ensureDefaultConfig() {
|
|
@@ -372,9 +475,20 @@ class DatabaseManager {
|
|
|
372
475
|
apiUrl: row.api_url,
|
|
373
476
|
apiKey: row.api_key,
|
|
374
477
|
sourceType: row.source_type,
|
|
478
|
+
authType: row.auth_type,
|
|
375
479
|
supportedModels: row.supported_models ? row.supported_models.split(',').map((model) => model.trim()).filter((model) => model.length > 0) : undefined,
|
|
376
480
|
modelLimits: row.model_limits ? JSON.parse(row.model_limits) : undefined,
|
|
377
481
|
enableProxy: row.enable_proxy === 1,
|
|
482
|
+
// Token超量配置
|
|
483
|
+
enableTokenLimit: row.enable_token_limit === 1,
|
|
484
|
+
tokenLimit: row.token_limit,
|
|
485
|
+
tokenResetInterval: row.token_reset_interval,
|
|
486
|
+
tokenResetBaseTime: row.token_reset_base_time,
|
|
487
|
+
// 请求次数超量配置
|
|
488
|
+
enableRequestLimit: row.enable_request_limit === 1,
|
|
489
|
+
requestCountLimit: row.request_count_limit,
|
|
490
|
+
requestResetInterval: row.request_reset_interval,
|
|
491
|
+
requestResetBaseTime: row.request_reset_base_time,
|
|
378
492
|
createdAt: row.created_at,
|
|
379
493
|
updatedAt: row.updated_at,
|
|
380
494
|
}));
|
|
@@ -388,15 +502,15 @@ class DatabaseManager {
|
|
|
388
502
|
const id = crypto_1.default.randomUUID();
|
|
389
503
|
const now = Date.now();
|
|
390
504
|
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);
|
|
505
|
+
.prepare('INSERT INTO api_services (id, vendor_id, name, api_url, api_key, source_type, auth_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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
|
506
|
+
.run(id, service.vendorId, service.name, service.apiUrl, service.apiKey, service.sourceType || null, service.authType || 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
507
|
return Object.assign(Object.assign({}, service), { id, createdAt: now, updatedAt: now });
|
|
394
508
|
}
|
|
395
509
|
updateAPIService(id, service) {
|
|
396
510
|
const now = Date.now();
|
|
397
511
|
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);
|
|
512
|
+
.prepare('UPDATE api_services SET vendor_id = ?, name = ?, api_url = ?, api_key = ?, source_type = ?, auth_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 = ?')
|
|
513
|
+
.run(service.vendorId, service.name, service.apiUrl, service.apiKey, service.sourceType || null, service.authType || 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
514
|
// 调试日志: 记录更新操作
|
|
401
515
|
if (result.changes > 0 && process.env.NODE_ENV === 'development') {
|
|
402
516
|
console.log(`[DB] Updated service ${id}: ${service.name} -> ${service.apiUrl}`);
|
|
@@ -472,23 +586,56 @@ class DatabaseManager {
|
|
|
472
586
|
totalTokensUsed: row.total_tokens_used,
|
|
473
587
|
resetInterval: row.reset_interval,
|
|
474
588
|
lastResetAt: row.last_reset_at,
|
|
589
|
+
tokenResetBaseTime: row.token_reset_base_time,
|
|
590
|
+
requestCountLimit: row.request_count_limit,
|
|
591
|
+
totalRequestsUsed: row.total_requests_used,
|
|
592
|
+
requestResetInterval: row.request_reset_interval,
|
|
593
|
+
requestLastResetAt: row.request_last_reset_at,
|
|
594
|
+
requestResetBaseTime: row.request_reset_base_time,
|
|
475
595
|
createdAt: row.created_at,
|
|
476
596
|
updatedAt: row.updated_at,
|
|
477
597
|
}));
|
|
478
598
|
}
|
|
599
|
+
getRule(id) {
|
|
600
|
+
const row = this.db.prepare('SELECT * FROM rules WHERE id = ?').get(id);
|
|
601
|
+
if (!row)
|
|
602
|
+
return undefined;
|
|
603
|
+
return {
|
|
604
|
+
id: row.id,
|
|
605
|
+
routeId: row.route_id,
|
|
606
|
+
contentType: row.content_type,
|
|
607
|
+
targetServiceId: row.target_service_id,
|
|
608
|
+
targetModel: row.target_model,
|
|
609
|
+
replacedModel: row.replaced_model,
|
|
610
|
+
sortOrder: row.sort_order,
|
|
611
|
+
timeout: row.timeout,
|
|
612
|
+
tokenLimit: row.token_limit,
|
|
613
|
+
totalTokensUsed: row.total_tokens_used,
|
|
614
|
+
resetInterval: row.reset_interval,
|
|
615
|
+
lastResetAt: row.last_reset_at,
|
|
616
|
+
tokenResetBaseTime: row.token_reset_base_time,
|
|
617
|
+
requestCountLimit: row.request_count_limit,
|
|
618
|
+
totalRequestsUsed: row.total_requests_used,
|
|
619
|
+
requestResetInterval: row.request_reset_interval,
|
|
620
|
+
requestLastResetAt: row.request_last_reset_at,
|
|
621
|
+
requestResetBaseTime: row.request_reset_base_time,
|
|
622
|
+
createdAt: row.created_at,
|
|
623
|
+
updatedAt: row.updated_at,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
479
626
|
createRule(route) {
|
|
480
627
|
const id = crypto_1.default.randomUUID();
|
|
481
628
|
const now = Date.now();
|
|
482
629
|
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);
|
|
630
|
+
.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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
|
631
|
+
.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
632
|
return Object.assign(Object.assign({}, route), { id, createdAt: now, updatedAt: now });
|
|
486
633
|
}
|
|
487
634
|
updateRule(id, route) {
|
|
488
635
|
const now = Date.now();
|
|
489
636
|
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);
|
|
637
|
+
.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 = ?')
|
|
638
|
+
.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
639
|
return result.changes > 0;
|
|
493
640
|
}
|
|
494
641
|
deleteRule(id) {
|
|
@@ -527,28 +674,133 @@ class DatabaseManager {
|
|
|
527
674
|
*/
|
|
528
675
|
checkAndResetRuleIfNeeded(ruleId) {
|
|
529
676
|
const rule = this.db
|
|
530
|
-
.prepare('SELECT reset_interval, last_reset_at FROM rules WHERE id = ?')
|
|
677
|
+
.prepare('SELECT reset_interval, last_reset_at, token_reset_base_time FROM rules WHERE id = ?')
|
|
531
678
|
.get(ruleId);
|
|
532
679
|
if (!rule || !rule.reset_interval) {
|
|
533
680
|
return false; // 没有设置重置间隔
|
|
534
681
|
}
|
|
535
682
|
const now = Date.now();
|
|
536
683
|
const resetIntervalMs = rule.reset_interval * 60 * 60 * 1000; // 小时转毫秒
|
|
684
|
+
const baseTime = rule.token_reset_base_time;
|
|
537
685
|
const lastResetAt = rule.last_reset_at || 0;
|
|
538
|
-
//
|
|
686
|
+
// 场景1: 设置了时间基点
|
|
687
|
+
if (baseTime) {
|
|
688
|
+
if (now >= baseTime) {
|
|
689
|
+
this.resetRuleTokenUsageWithBaseTime(ruleId, baseTime);
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
// 场景2: 未设置时间基点,使用原始逻辑(向后兼容)
|
|
539
695
|
if (now - lastResetAt >= resetIntervalMs) {
|
|
540
696
|
this.resetRuleTokenUsage(ruleId);
|
|
541
697
|
return true;
|
|
542
698
|
}
|
|
543
699
|
return false;
|
|
544
700
|
}
|
|
701
|
+
/**
|
|
702
|
+
* 重置规则的Token使用量(带时间基点更新)
|
|
703
|
+
*/
|
|
704
|
+
resetRuleTokenUsageWithBaseTime(ruleId, currentBaseTime) {
|
|
705
|
+
const now = Date.now();
|
|
706
|
+
const rule = this.db
|
|
707
|
+
.prepare('SELECT reset_interval FROM rules WHERE id = ?')
|
|
708
|
+
.get(ruleId);
|
|
709
|
+
if (!rule || !rule.reset_interval) {
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
const resetIntervalMs = rule.reset_interval * 60 * 60 * 1000;
|
|
713
|
+
// 计算下一个时间基点
|
|
714
|
+
let nextBaseTime = currentBaseTime;
|
|
715
|
+
while (nextBaseTime <= now) {
|
|
716
|
+
nextBaseTime += resetIntervalMs;
|
|
717
|
+
}
|
|
718
|
+
const result = this.db
|
|
719
|
+
.prepare('UPDATE rules SET total_tokens_used = 0, last_reset_at = ?, token_reset_base_time = ? WHERE id = ?')
|
|
720
|
+
.run(now, nextBaseTime, ruleId);
|
|
721
|
+
return result.changes > 0;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* 增加规则的请求次数
|
|
725
|
+
* @param ruleId 规则ID
|
|
726
|
+
* @param count 增加的次数
|
|
727
|
+
* @returns 是否成功
|
|
728
|
+
*/
|
|
729
|
+
incrementRuleRequestCount(ruleId, count) {
|
|
730
|
+
const result = this.db
|
|
731
|
+
.prepare('UPDATE rules SET total_requests_used = total_requests_used + ? WHERE id = ?')
|
|
732
|
+
.run(count, ruleId);
|
|
733
|
+
return result.changes > 0;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* 重置规则的请求次数
|
|
737
|
+
* @param ruleId 规则ID
|
|
738
|
+
* @returns 是否成功
|
|
739
|
+
*/
|
|
740
|
+
resetRuleRequestCount(ruleId) {
|
|
741
|
+
const now = Date.now();
|
|
742
|
+
const result = this.db
|
|
743
|
+
.prepare('UPDATE rules SET total_requests_used = 0, request_last_reset_at = ? WHERE id = ?')
|
|
744
|
+
.run(now, ruleId);
|
|
745
|
+
return result.changes > 0;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* 检查并重置到期的规则(请求次数)
|
|
749
|
+
* 如果规则设置了request_reset_interval且已经到了重置时间,则自动重置请求次数
|
|
750
|
+
* @param ruleId 规则ID
|
|
751
|
+
* @returns 是否进行了重置
|
|
752
|
+
*/
|
|
753
|
+
checkAndResetRequestCountIfNeeded(ruleId) {
|
|
754
|
+
const rule = this.db
|
|
755
|
+
.prepare('SELECT request_reset_interval, request_last_reset_at, request_reset_base_time FROM rules WHERE id = ?')
|
|
756
|
+
.get(ruleId);
|
|
757
|
+
if (!rule || !rule.request_reset_interval) {
|
|
758
|
+
return false; // 没有设置重置间隔
|
|
759
|
+
}
|
|
760
|
+
const now = Date.now();
|
|
761
|
+
const resetIntervalMs = rule.request_reset_interval * 60 * 60 * 1000; // 小时转毫秒
|
|
762
|
+
const baseTime = rule.request_reset_base_time;
|
|
763
|
+
const lastResetAt = rule.request_last_reset_at || 0;
|
|
764
|
+
// 场景1: 设置了时间基点
|
|
765
|
+
if (baseTime) {
|
|
766
|
+
if (now >= baseTime) {
|
|
767
|
+
this.resetRuleRequestCountWithBaseTime(ruleId, baseTime);
|
|
768
|
+
return true;
|
|
769
|
+
}
|
|
770
|
+
return false;
|
|
771
|
+
}
|
|
772
|
+
// 场景2: 未设置时间基点,使用原始逻辑(向后兼容)
|
|
773
|
+
if (now - lastResetAt >= resetIntervalMs) {
|
|
774
|
+
this.resetRuleRequestCount(ruleId);
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* 重置规则的请求次数(带时间基点更新)
|
|
781
|
+
*/
|
|
782
|
+
resetRuleRequestCountWithBaseTime(ruleId, currentBaseTime) {
|
|
783
|
+
const now = Date.now();
|
|
784
|
+
const rule = this.db
|
|
785
|
+
.prepare('SELECT request_reset_interval FROM rules WHERE id = ?')
|
|
786
|
+
.get(ruleId);
|
|
787
|
+
if (!rule || !rule.request_reset_interval) {
|
|
788
|
+
return false;
|
|
789
|
+
}
|
|
790
|
+
const resetIntervalMs = rule.request_reset_interval * 60 * 60 * 1000;
|
|
791
|
+
// 计算下一个时间基点
|
|
792
|
+
let nextBaseTime = currentBaseTime;
|
|
793
|
+
while (nextBaseTime <= now) {
|
|
794
|
+
nextBaseTime += resetIntervalMs;
|
|
795
|
+
}
|
|
796
|
+
const result = this.db
|
|
797
|
+
.prepare('UPDATE rules SET total_requests_used = 0, request_last_reset_at = ?, request_reset_base_time = ? WHERE id = ?')
|
|
798
|
+
.run(now, nextBaseTime, ruleId);
|
|
799
|
+
return result.changes > 0;
|
|
800
|
+
}
|
|
545
801
|
// Log operations
|
|
546
802
|
addLog(log) {
|
|
547
803
|
return __awaiter(this, void 0, void 0, function* () {
|
|
548
|
-
const { path } = log;
|
|
549
|
-
if (!path.startsWith('/v1/')) {
|
|
550
|
-
return;
|
|
551
|
-
}
|
|
552
804
|
const id = crypto_1.default.randomUUID();
|
|
553
805
|
yield this.logDb.put(id, JSON.stringify(Object.assign(Object.assign({}, log), { id })));
|
|
554
806
|
// 清除缓存
|
|
@@ -587,55 +839,6 @@ class DatabaseManager {
|
|
|
587
839
|
this.logsCountCache = null;
|
|
588
840
|
});
|
|
589
841
|
}
|
|
590
|
-
// Access log operations
|
|
591
|
-
addAccessLog(log) {
|
|
592
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
593
|
-
const id = crypto_1.default.randomUUID();
|
|
594
|
-
yield this.accessLogDb.put(id, JSON.stringify(Object.assign(Object.assign({}, log), { id })));
|
|
595
|
-
// 清除缓存
|
|
596
|
-
this.accessLogsCountCache = null;
|
|
597
|
-
return id;
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
updateAccessLog(id, data) {
|
|
601
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
602
|
-
const log = yield this.accessLogDb.get(id);
|
|
603
|
-
const updatedLog = Object.assign(Object.assign({}, JSON.parse(log)), data);
|
|
604
|
-
yield this.accessLogDb.put(id, JSON.stringify(updatedLog));
|
|
605
|
-
});
|
|
606
|
-
}
|
|
607
|
-
getAccessLogs() {
|
|
608
|
-
return __awaiter(this, arguments, void 0, function* (limit = 100, offset = 0) {
|
|
609
|
-
var _a, e_2, _b, _c;
|
|
610
|
-
const allLogs = [];
|
|
611
|
-
try {
|
|
612
|
-
for (var _d = true, _e = __asyncValues(this.accessLogDb.iterator()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
613
|
-
_c = _f.value;
|
|
614
|
-
_d = false;
|
|
615
|
-
const [, value] = _c;
|
|
616
|
-
allLogs.push(JSON.parse(value));
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
620
|
-
finally {
|
|
621
|
-
try {
|
|
622
|
-
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
623
|
-
}
|
|
624
|
-
finally { if (e_2) throw e_2.error; }
|
|
625
|
-
}
|
|
626
|
-
// Sort by timestamp in descending order (newest first)
|
|
627
|
-
allLogs.sort((a, b) => b.timestamp - a.timestamp);
|
|
628
|
-
// Apply offset and limit
|
|
629
|
-
return allLogs.slice(offset, offset + limit);
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
clearAccessLogs() {
|
|
633
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
634
|
-
yield this.accessLogDb.clear();
|
|
635
|
-
// 清除缓存
|
|
636
|
-
this.accessLogsCountCache = null;
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
842
|
// Error log operations
|
|
640
843
|
addErrorLog(log) {
|
|
641
844
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -647,7 +850,7 @@ class DatabaseManager {
|
|
|
647
850
|
}
|
|
648
851
|
getErrorLogs() {
|
|
649
852
|
return __awaiter(this, arguments, void 0, function* (limit = 100, offset = 0) {
|
|
650
|
-
var _a,
|
|
853
|
+
var _a, e_2, _b, _c;
|
|
651
854
|
const allLogs = [];
|
|
652
855
|
try {
|
|
653
856
|
for (var _d = true, _e = __asyncValues(this.errorLogDb.iterator()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
@@ -657,12 +860,12 @@ class DatabaseManager {
|
|
|
657
860
|
allLogs.push(JSON.parse(value));
|
|
658
861
|
}
|
|
659
862
|
}
|
|
660
|
-
catch (
|
|
863
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
661
864
|
finally {
|
|
662
865
|
try {
|
|
663
866
|
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
664
867
|
}
|
|
665
|
-
finally { if (
|
|
868
|
+
finally { if (e_2) throw e_2.error; }
|
|
666
869
|
}
|
|
667
870
|
// Sort by timestamp in descending order (newest first)
|
|
668
871
|
allLogs.sort((a, b) => b.timestamp - a.timestamp);
|
|
@@ -682,7 +885,7 @@ class DatabaseManager {
|
|
|
682
885
|
*/
|
|
683
886
|
getLogsCount() {
|
|
684
887
|
return __awaiter(this, void 0, void 0, function* () {
|
|
685
|
-
var _a,
|
|
888
|
+
var _a, e_3, _b, _c;
|
|
686
889
|
const now = Date.now();
|
|
687
890
|
if (this.logsCountCache && now - this.logsCountCache.timestamp < this.CACHE_TTL) {
|
|
688
891
|
return this.logsCountCache.count;
|
|
@@ -696,53 +899,23 @@ class DatabaseManager {
|
|
|
696
899
|
count++;
|
|
697
900
|
}
|
|
698
901
|
}
|
|
699
|
-
catch (
|
|
902
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
700
903
|
finally {
|
|
701
904
|
try {
|
|
702
905
|
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
703
906
|
}
|
|
704
|
-
finally { if (
|
|
907
|
+
finally { if (e_3) throw e_3.error; }
|
|
705
908
|
}
|
|
706
909
|
this.logsCountCache = { count, timestamp: now };
|
|
707
910
|
return count;
|
|
708
911
|
});
|
|
709
912
|
}
|
|
710
|
-
/**
|
|
711
|
-
* 获取访问日志总数(带缓存)
|
|
712
|
-
*/
|
|
713
|
-
getAccessLogsCount() {
|
|
714
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
715
|
-
var _a, e_5, _b, _c;
|
|
716
|
-
const now = Date.now();
|
|
717
|
-
if (this.accessLogsCountCache && now - this.accessLogsCountCache.timestamp < this.CACHE_TTL) {
|
|
718
|
-
return this.accessLogsCountCache.count;
|
|
719
|
-
}
|
|
720
|
-
let count = 0;
|
|
721
|
-
try {
|
|
722
|
-
for (var _d = true, _e = __asyncValues(this.accessLogDb.iterator()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
723
|
-
_c = _f.value;
|
|
724
|
-
_d = false;
|
|
725
|
-
const _ = _c;
|
|
726
|
-
count++;
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
730
|
-
finally {
|
|
731
|
-
try {
|
|
732
|
-
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
733
|
-
}
|
|
734
|
-
finally { if (e_5) throw e_5.error; }
|
|
735
|
-
}
|
|
736
|
-
this.accessLogsCountCache = { count, timestamp: now };
|
|
737
|
-
return count;
|
|
738
|
-
});
|
|
739
|
-
}
|
|
740
913
|
/**
|
|
741
914
|
* 获取错误日志总数(带缓存)
|
|
742
915
|
*/
|
|
743
916
|
getErrorLogsCount() {
|
|
744
917
|
return __awaiter(this, void 0, void 0, function* () {
|
|
745
|
-
var _a,
|
|
918
|
+
var _a, e_4, _b, _c;
|
|
746
919
|
const now = Date.now();
|
|
747
920
|
if (this.errorLogsCountCache && now - this.errorLogsCountCache.timestamp < this.CACHE_TTL) {
|
|
748
921
|
return this.errorLogsCountCache.count;
|
|
@@ -756,12 +929,12 @@ class DatabaseManager {
|
|
|
756
929
|
count++;
|
|
757
930
|
}
|
|
758
931
|
}
|
|
759
|
-
catch (
|
|
932
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
760
933
|
finally {
|
|
761
934
|
try {
|
|
762
935
|
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
763
936
|
}
|
|
764
|
-
finally { if (
|
|
937
|
+
finally { if (e_4) throw e_4.error; }
|
|
765
938
|
}
|
|
766
939
|
this.errorLogsCountCache = { count, timestamp: now };
|
|
767
940
|
return count;
|
|
@@ -790,7 +963,7 @@ class DatabaseManager {
|
|
|
790
963
|
}
|
|
791
964
|
});
|
|
792
965
|
}
|
|
793
|
-
addToBlacklist(serviceId, routeId, contentType, errorMessage, statusCode) {
|
|
966
|
+
addToBlacklist(serviceId, routeId, contentType, errorMessage, statusCode, errorType) {
|
|
794
967
|
return __awaiter(this, void 0, void 0, function* () {
|
|
795
968
|
const key = `${routeId}:${contentType}:${serviceId}`;
|
|
796
969
|
const now = Date.now();
|
|
@@ -804,6 +977,7 @@ class DatabaseManager {
|
|
|
804
977
|
entry.errorCount++;
|
|
805
978
|
entry.lastError = errorMessage;
|
|
806
979
|
entry.lastStatusCode = statusCode;
|
|
980
|
+
entry.errorType = errorType;
|
|
807
981
|
yield this.blacklistDb.put(key, JSON.stringify(entry));
|
|
808
982
|
}
|
|
809
983
|
catch (error) {
|
|
@@ -818,6 +992,7 @@ class DatabaseManager {
|
|
|
818
992
|
errorCount: 1,
|
|
819
993
|
lastError: errorMessage,
|
|
820
994
|
lastStatusCode: statusCode,
|
|
995
|
+
errorType,
|
|
821
996
|
};
|
|
822
997
|
yield this.blacklistDb.put(key, JSON.stringify(entry));
|
|
823
998
|
}
|
|
@@ -827,9 +1002,15 @@ class DatabaseManager {
|
|
|
827
1002
|
}
|
|
828
1003
|
});
|
|
829
1004
|
}
|
|
1005
|
+
removeFromBlacklist(serviceId, routeId, contentType) {
|
|
1006
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1007
|
+
const key = `${routeId}:${contentType}:${serviceId}`;
|
|
1008
|
+
yield this.blacklistDb.del(key);
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
830
1011
|
cleanupExpiredBlacklist() {
|
|
831
1012
|
return __awaiter(this, void 0, void 0, function* () {
|
|
832
|
-
var _a,
|
|
1013
|
+
var _a, e_5, _b, _c;
|
|
833
1014
|
const now = Date.now();
|
|
834
1015
|
let count = 0;
|
|
835
1016
|
try {
|
|
@@ -844,12 +1025,12 @@ class DatabaseManager {
|
|
|
844
1025
|
}
|
|
845
1026
|
}
|
|
846
1027
|
}
|
|
847
|
-
catch (
|
|
1028
|
+
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
848
1029
|
finally {
|
|
849
1030
|
try {
|
|
850
1031
|
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
851
1032
|
}
|
|
852
|
-
finally { if (
|
|
1033
|
+
finally { if (e_5) throw e_5.error; }
|
|
853
1034
|
}
|
|
854
1035
|
return count;
|
|
855
1036
|
});
|
|
@@ -902,8 +1083,8 @@ class DatabaseManager {
|
|
|
902
1083
|
// Import API services
|
|
903
1084
|
for (const service of importData.apiServices) {
|
|
904
1085
|
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);
|
|
1086
|
+
.prepare('INSERT INTO api_services (id, vendor_id, name, api_url, api_key, source_type, auth_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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
|
1087
|
+
.run(service.id, service.vendorId, service.name, service.apiUrl, service.apiKey, service.sourceType || null, service.authType || 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
1088
|
}
|
|
908
1089
|
// Import routes
|
|
909
1090
|
for (const route of importData.routes) {
|
|
@@ -914,8 +1095,8 @@ class DatabaseManager {
|
|
|
914
1095
|
// Import rules
|
|
915
1096
|
for (const rule of importData.rules) {
|
|
916
1097
|
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);
|
|
1098
|
+
.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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
|
1099
|
+
.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
1100
|
}
|
|
920
1101
|
// Update config
|
|
921
1102
|
this.updateConfig(importData.config);
|
|
@@ -930,7 +1111,7 @@ class DatabaseManager {
|
|
|
930
1111
|
// Statistics operations
|
|
931
1112
|
getStatistics() {
|
|
932
1113
|
return __awaiter(this, arguments, void 0, function* (days = 30) {
|
|
933
|
-
var _a,
|
|
1114
|
+
var _a, e_6, _b, _c, _d, e_7, _e, _f;
|
|
934
1115
|
var _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
935
1116
|
const now = Date.now();
|
|
936
1117
|
const startTime = now - days * 24 * 60 * 60 * 1000;
|
|
@@ -947,12 +1128,12 @@ class DatabaseManager {
|
|
|
947
1128
|
}
|
|
948
1129
|
}
|
|
949
1130
|
}
|
|
950
|
-
catch (
|
|
1131
|
+
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
|
951
1132
|
finally {
|
|
952
1133
|
try {
|
|
953
1134
|
if (!_z && !_a && (_b = _0.return)) yield _b.call(_0);
|
|
954
1135
|
}
|
|
955
|
-
finally { if (
|
|
1136
|
+
finally { if (e_6) throw e_6.error; }
|
|
956
1137
|
}
|
|
957
1138
|
// Get all error logs
|
|
958
1139
|
const errorLogs = [];
|
|
@@ -970,12 +1151,12 @@ class DatabaseManager {
|
|
|
970
1151
|
}
|
|
971
1152
|
}
|
|
972
1153
|
}
|
|
973
|
-
catch (
|
|
1154
|
+
catch (e_7_1) { e_7 = { error: e_7_1 }; }
|
|
974
1155
|
finally {
|
|
975
1156
|
try {
|
|
976
1157
|
if (!_2 && !_d && (_e = _3.return)) yield _e.call(_3);
|
|
977
1158
|
}
|
|
978
|
-
finally { if (
|
|
1159
|
+
finally { if (e_7) throw e_7.error; }
|
|
979
1160
|
}
|
|
980
1161
|
// Get vendors and services for mapping
|
|
981
1162
|
const vendors = this.getVendors();
|
|
@@ -1150,10 +1331,180 @@ class DatabaseManager {
|
|
|
1150
1331
|
};
|
|
1151
1332
|
});
|
|
1152
1333
|
}
|
|
1334
|
+
getRuleBlacklistStatus(serviceId, routeId, contentType) {
|
|
1335
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1336
|
+
const key = `${routeId}:${contentType}:${serviceId}`;
|
|
1337
|
+
try {
|
|
1338
|
+
const value = yield this.blacklistDb.get(key);
|
|
1339
|
+
const entry = JSON.parse(value);
|
|
1340
|
+
// 检查是否过期
|
|
1341
|
+
if (Date.now() > entry.expiresAt) {
|
|
1342
|
+
yield this.blacklistDb.del(key);
|
|
1343
|
+
return null;
|
|
1344
|
+
}
|
|
1345
|
+
return entry;
|
|
1346
|
+
}
|
|
1347
|
+
catch (error) {
|
|
1348
|
+
if (error.code === 'LEVEL_NOT_FOUND') {
|
|
1349
|
+
return null;
|
|
1350
|
+
}
|
|
1351
|
+
throw error;
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
// Session operations
|
|
1356
|
+
/**
|
|
1357
|
+
* 创建或更新 session
|
|
1358
|
+
* 当有新的请求日志时调用此方法来更新 session 信息
|
|
1359
|
+
*/
|
|
1360
|
+
upsertSession(session) {
|
|
1361
|
+
const now = Date.now();
|
|
1362
|
+
const existing = this.db.prepare('SELECT * FROM sessions WHERE id = ?').get(session.id);
|
|
1363
|
+
if (existing) {
|
|
1364
|
+
// 更新现有 session
|
|
1365
|
+
this.db.prepare(`
|
|
1366
|
+
UPDATE sessions SET
|
|
1367
|
+
last_request_at = ?,
|
|
1368
|
+
request_count = request_count + 1,
|
|
1369
|
+
total_tokens = total_tokens + ?,
|
|
1370
|
+
vendor_id = ?,
|
|
1371
|
+
vendor_name = ?,
|
|
1372
|
+
service_id = ?,
|
|
1373
|
+
service_name = ?,
|
|
1374
|
+
model = ?
|
|
1375
|
+
WHERE id = ?
|
|
1376
|
+
`).run(now, session.totalTokens || 0, session.vendorId || null, session.vendorName || null, session.serviceId || null, session.serviceName || null, session.model || null, session.id);
|
|
1377
|
+
}
|
|
1378
|
+
else {
|
|
1379
|
+
// 创建新 session
|
|
1380
|
+
this.db.prepare(`
|
|
1381
|
+
INSERT INTO sessions (id, target_type, title, first_request_at, last_request_at, request_count, total_tokens, vendor_id, vendor_name, service_id, service_name, model)
|
|
1382
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1383
|
+
`).run(session.id, session.targetType, session.title || null, session.firstRequestAt, now, 1, session.totalTokens || 0, session.vendorId || null, session.vendorName || null, session.serviceId || null, session.serviceName || null, session.model || null);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* 获取所有 sessions
|
|
1388
|
+
*/
|
|
1389
|
+
getSessions(limit = 100, offset = 0) {
|
|
1390
|
+
const rows = this.db.prepare('SELECT * FROM sessions ORDER BY last_request_at DESC LIMIT ? OFFSET ?').all(limit, offset);
|
|
1391
|
+
return rows.map((row) => ({
|
|
1392
|
+
id: row.id,
|
|
1393
|
+
targetType: row.target_type,
|
|
1394
|
+
title: row.title,
|
|
1395
|
+
firstRequestAt: row.first_request_at,
|
|
1396
|
+
lastRequestAt: row.last_request_at,
|
|
1397
|
+
requestCount: row.request_count,
|
|
1398
|
+
totalTokens: row.total_tokens,
|
|
1399
|
+
vendorId: row.vendor_id,
|
|
1400
|
+
vendorName: row.vendor_name,
|
|
1401
|
+
serviceId: row.service_id,
|
|
1402
|
+
serviceName: row.service_name,
|
|
1403
|
+
model: row.model,
|
|
1404
|
+
}));
|
|
1405
|
+
}
|
|
1406
|
+
/**
|
|
1407
|
+
* 根据 session ID 获取 session
|
|
1408
|
+
*/
|
|
1409
|
+
getSession(id) {
|
|
1410
|
+
const row = this.db.prepare('SELECT * FROM sessions WHERE id = ?').get(id);
|
|
1411
|
+
if (!row)
|
|
1412
|
+
return null;
|
|
1413
|
+
return {
|
|
1414
|
+
id: row.id,
|
|
1415
|
+
targetType: row.target_type,
|
|
1416
|
+
title: row.title,
|
|
1417
|
+
firstRequestAt: row.first_request_at,
|
|
1418
|
+
lastRequestAt: row.last_request_at,
|
|
1419
|
+
requestCount: row.request_count,
|
|
1420
|
+
totalTokens: row.total_tokens,
|
|
1421
|
+
vendorId: row.vendor_id,
|
|
1422
|
+
vendorName: row.vendor_name,
|
|
1423
|
+
serviceId: row.service_id,
|
|
1424
|
+
serviceName: row.service_name,
|
|
1425
|
+
model: row.model,
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1429
|
+
* 获取 sessions 总数
|
|
1430
|
+
*/
|
|
1431
|
+
getSessionsCount() {
|
|
1432
|
+
const result = this.db.prepare('SELECT COUNT(*) as count FROM sessions').get();
|
|
1433
|
+
return result.count;
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* 删除指定 session
|
|
1437
|
+
*/
|
|
1438
|
+
deleteSession(id) {
|
|
1439
|
+
const result = this.db.prepare('DELETE FROM sessions WHERE id = ?').run(id);
|
|
1440
|
+
return result.changes > 0;
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* 清空所有 sessions
|
|
1444
|
+
*/
|
|
1445
|
+
clearSessions() {
|
|
1446
|
+
this.db.prepare('DELETE FROM sessions').run();
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* 获取指定 session 的请求日志
|
|
1450
|
+
* @param sessionId session ID
|
|
1451
|
+
* @param limit 限制数量
|
|
1452
|
+
*/
|
|
1453
|
+
getLogsBySessionId(sessionId_1) {
|
|
1454
|
+
return __awaiter(this, arguments, void 0, function* (sessionId, limit = 100) {
|
|
1455
|
+
var _a, e_8, _b, _c;
|
|
1456
|
+
// 从 LevelDB 中读取所有日志
|
|
1457
|
+
const allLogs = [];
|
|
1458
|
+
try {
|
|
1459
|
+
for (var _d = true, _e = __asyncValues(this.logDb.iterator()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
1460
|
+
_c = _f.value;
|
|
1461
|
+
_d = false;
|
|
1462
|
+
const [, value] = _c;
|
|
1463
|
+
const log = JSON.parse(value);
|
|
1464
|
+
// 检查日志是否属于该 session(通过 headers 中的 session_id 或 body 中的 metadata.user_id)
|
|
1465
|
+
if (this.isLogBelongsToSession(log, sessionId)) {
|
|
1466
|
+
allLogs.push(log);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
catch (e_8_1) { e_8 = { error: e_8_1 }; }
|
|
1471
|
+
finally {
|
|
1472
|
+
try {
|
|
1473
|
+
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
1474
|
+
}
|
|
1475
|
+
finally { if (e_8) throw e_8.error; }
|
|
1476
|
+
}
|
|
1477
|
+
// 按时间倒序排序并限制数量
|
|
1478
|
+
allLogs.sort((a, b) => b.timestamp - a.timestamp);
|
|
1479
|
+
return allLogs.slice(0, limit);
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* 检查日志是否属于指定 session
|
|
1484
|
+
*/
|
|
1485
|
+
isLogBelongsToSession(log, sessionId) {
|
|
1486
|
+
var _a, _b;
|
|
1487
|
+
// 检查 headers 中的 session_id(Codex)
|
|
1488
|
+
if (((_a = log.headers) === null || _a === void 0 ? void 0 : _a['session_id']) === sessionId) {
|
|
1489
|
+
return true;
|
|
1490
|
+
}
|
|
1491
|
+
// 检查 body 中的 metadata.user_id(Claude Code)
|
|
1492
|
+
if (log.body) {
|
|
1493
|
+
try {
|
|
1494
|
+
const body = JSON.parse(log.body);
|
|
1495
|
+
if (((_b = body.metadata) === null || _b === void 0 ? void 0 : _b.user_id) === sessionId) {
|
|
1496
|
+
return true;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
catch (_c) {
|
|
1500
|
+
// 忽略解析错误
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
return false;
|
|
1504
|
+
}
|
|
1153
1505
|
close() {
|
|
1154
1506
|
this.db.close();
|
|
1155
1507
|
this.logDb.close();
|
|
1156
|
-
this.accessLogDb.close();
|
|
1157
1508
|
this.errorLogDb.close();
|
|
1158
1509
|
this.blacklistDb.close();
|
|
1159
1510
|
}
|