aicodeswitch 1.8.0 → 1.10.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/.github/workflows/publish-to-npm.yaml +34 -0
- package/CHANGELOG.md +15 -0
- package/dist/server/database.js +308 -31
- package/dist/server/main.js +13 -0
- package/dist/server/proxy-server.js +127 -12
- package/dist/ui/assets/index-BdKga7KO.js +391 -0
- package/dist/ui/assets/index-Dat4drax.css +1 -0
- package/dist/ui/index.html +2 -2
- package/package.json +5 -2
- package/dist/ui/assets/index-D6QkIxk8.js +0 -391
- package/dist/ui/assets/index-g31C4Oez.css +0 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Publish To NPM
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
types:
|
|
5
|
+
- closed
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write
|
|
13
|
+
id-token: write
|
|
14
|
+
if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || (github.event_name == 'pull_request' && github.event.pull_request.merged == true)
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v5
|
|
17
|
+
- uses: actions/setup-node@v4
|
|
18
|
+
with:
|
|
19
|
+
node-version: '24.x'
|
|
20
|
+
registry-url: 'https://registry.npmjs.org'
|
|
21
|
+
- run: npm install --verbose
|
|
22
|
+
- run: |
|
|
23
|
+
git config --global user.name 'github-actions[bot]'
|
|
24
|
+
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
|
25
|
+
- run: npm run release
|
|
26
|
+
- run: npx can-npm-publish
|
|
27
|
+
- run: npm publish --provenance
|
|
28
|
+
env:
|
|
29
|
+
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
30
|
+
- uses: ad-m/github-push-action@master
|
|
31
|
+
with:
|
|
32
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
33
|
+
branch: main
|
|
34
|
+
tags: true
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
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
|
+
|
|
5
|
+
### 1.10.1 (2026-01-25)
|
|
6
|
+
|
|
7
|
+
## [1.10.0](https://github.com/tangshuang/aicodeswitch/compare/v1.9.0...v1.10.0) (2026-01-25)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* 日志分页 ([0e68786](https://github.com/tangshuang/aicodeswitch/commit/0e68786b4e5bdce9bf7fc5ce85a1d4bdaf5a710c))
|
|
13
|
+
* 新增了代理能力 ([d6254ae](https://github.com/tangshuang/aicodeswitch/commit/d6254ae463f01f583601ed71f64f346b63718853))
|
|
14
|
+
* 优化细节 ([e4bdaef](https://github.com/tangshuang/aicodeswitch/commit/e4bdaef14bb0088e7feb5371490124c8dd169fdd))
|
|
15
|
+
* github workflows ([37dd371](https://github.com/tangshuang/aicodeswitch/commit/37dd3717e79dab1c4aec99b762774cc549f1efc8))
|
package/dist/server/database.js
CHANGED
|
@@ -57,6 +57,31 @@ class DatabaseManager {
|
|
|
57
57
|
writable: true,
|
|
58
58
|
value: void 0
|
|
59
59
|
});
|
|
60
|
+
// 缓存机制:总数查询缓存
|
|
61
|
+
Object.defineProperty(this, "logsCountCache", {
|
|
62
|
+
enumerable: true,
|
|
63
|
+
configurable: true,
|
|
64
|
+
writable: true,
|
|
65
|
+
value: null
|
|
66
|
+
});
|
|
67
|
+
Object.defineProperty(this, "accessLogsCountCache", {
|
|
68
|
+
enumerable: true,
|
|
69
|
+
configurable: true,
|
|
70
|
+
writable: true,
|
|
71
|
+
value: null
|
|
72
|
+
});
|
|
73
|
+
Object.defineProperty(this, "errorLogsCountCache", {
|
|
74
|
+
enumerable: true,
|
|
75
|
+
configurable: true,
|
|
76
|
+
writable: true,
|
|
77
|
+
value: null
|
|
78
|
+
});
|
|
79
|
+
Object.defineProperty(this, "CACHE_TTL", {
|
|
80
|
+
enumerable: true,
|
|
81
|
+
configurable: true,
|
|
82
|
+
writable: true,
|
|
83
|
+
value: 1000
|
|
84
|
+
}); // 1秒缓存TTL
|
|
60
85
|
this.db = new better_sqlite3_1.default(path_1.default.join(dataPath, 'app.db'));
|
|
61
86
|
// 启用外键约束(SQLite 默认禁用,必须手动启用才能使 ON DELETE CASCADE 生效)
|
|
62
87
|
this.db.pragma('foreign_keys = ON');
|
|
@@ -100,6 +125,61 @@ class DatabaseManager {
|
|
|
100
125
|
console.log('[DB] Migration completed: model_limits column added');
|
|
101
126
|
}
|
|
102
127
|
}
|
|
128
|
+
// 检查vendors表是否有sort_order字段
|
|
129
|
+
const vendorsColumns = this.db.pragma('table_info(vendors)');
|
|
130
|
+
const hasSortOrder = vendorsColumns.some((col) => col.name === 'sort_order');
|
|
131
|
+
if (!hasSortOrder) {
|
|
132
|
+
console.log('[DB] Running migration: Adding sort_order column to vendors table');
|
|
133
|
+
this.db.exec('ALTER TABLE vendors ADD COLUMN sort_order INTEGER DEFAULT 0;');
|
|
134
|
+
console.log('[DB] Migration completed: sort_order column added to vendors');
|
|
135
|
+
}
|
|
136
|
+
// 检查rules表是否有token相关字段
|
|
137
|
+
const rulesColumns = this.db.pragma('table_info(rules)');
|
|
138
|
+
const hasTokenLimit = rulesColumns.some((col) => col.name === 'token_limit');
|
|
139
|
+
const hasTotalTokensUsed = rulesColumns.some((col) => col.name === 'total_tokens_used');
|
|
140
|
+
const hasResetInterval = rulesColumns.some((col) => col.name === 'reset_interval');
|
|
141
|
+
const hasLastResetAt = rulesColumns.some((col) => col.name === 'last_reset_at');
|
|
142
|
+
if (!hasTokenLimit) {
|
|
143
|
+
console.log('[DB] Running migration: Adding token_limit column to rules table');
|
|
144
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN token_limit INTEGER;');
|
|
145
|
+
console.log('[DB] Migration completed: token_limit column added');
|
|
146
|
+
}
|
|
147
|
+
if (!hasTotalTokensUsed) {
|
|
148
|
+
console.log('[DB] Running migration: Adding total_tokens_used column to rules table');
|
|
149
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN total_tokens_used INTEGER DEFAULT 0;');
|
|
150
|
+
console.log('[DB] Migration completed: total_tokens_used column added');
|
|
151
|
+
}
|
|
152
|
+
if (!hasResetInterval) {
|
|
153
|
+
console.log('[DB] Running migration: Adding reset_interval column to rules table');
|
|
154
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN reset_interval INTEGER;');
|
|
155
|
+
console.log('[DB] Migration completed: reset_interval column added');
|
|
156
|
+
}
|
|
157
|
+
if (!hasLastResetAt) {
|
|
158
|
+
console.log('[DB] Running migration: Adding last_reset_at column to rules table');
|
|
159
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN last_reset_at INTEGER;');
|
|
160
|
+
console.log('[DB] Migration completed: last_reset_at column added');
|
|
161
|
+
}
|
|
162
|
+
// 检查rules表是否有timeout字段
|
|
163
|
+
const hasRuleTimeout = rulesColumns.some((col) => col.name === 'timeout');
|
|
164
|
+
if (!hasRuleTimeout) {
|
|
165
|
+
console.log('[DB] Running migration: Adding timeout column to rules table');
|
|
166
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN timeout INTEGER;');
|
|
167
|
+
console.log('[DB] Migration completed: timeout column added to rules');
|
|
168
|
+
}
|
|
169
|
+
// 检查api_services表是否有timeout字段,如果有则移除
|
|
170
|
+
const hasServiceTimeout = columns.some((col) => col.name === 'timeout');
|
|
171
|
+
if (hasServiceTimeout) {
|
|
172
|
+
console.log('[DB] Running migration: Removing timeout column from api_services table');
|
|
173
|
+
yield this.migrateRemoveServiceTimeout();
|
|
174
|
+
console.log('[DB] Migration completed: timeout column removed from api_services');
|
|
175
|
+
}
|
|
176
|
+
// 检查api_services表是否有enable_proxy字段
|
|
177
|
+
const hasEnableProxy = columns.some((col) => col.name === 'enable_proxy');
|
|
178
|
+
if (!hasEnableProxy) {
|
|
179
|
+
console.log('[DB] Running migration: Adding enable_proxy column to api_services table');
|
|
180
|
+
this.db.exec('ALTER TABLE api_services ADD COLUMN enable_proxy INTEGER DEFAULT 0;');
|
|
181
|
+
console.log('[DB] Migration completed: enable_proxy column added');
|
|
182
|
+
}
|
|
103
183
|
});
|
|
104
184
|
}
|
|
105
185
|
migrateMaxOutputTokensToModelLimits() {
|
|
@@ -138,12 +218,42 @@ class DatabaseManager {
|
|
|
138
218
|
console.log('[DB] Migration completed: Replaced max_output_tokens with model_limits');
|
|
139
219
|
});
|
|
140
220
|
}
|
|
221
|
+
migrateRemoveServiceTimeout() {
|
|
222
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
223
|
+
// SQLite 不支持直接删除列,需要重建表
|
|
224
|
+
this.db.pragma('foreign_keys = OFF');
|
|
225
|
+
this.db.exec(`
|
|
226
|
+
CREATE TABLE api_services_new (
|
|
227
|
+
id TEXT PRIMARY KEY,
|
|
228
|
+
vendor_id TEXT NOT NULL,
|
|
229
|
+
name TEXT NOT NULL,
|
|
230
|
+
api_url TEXT NOT NULL,
|
|
231
|
+
api_key TEXT NOT NULL,
|
|
232
|
+
source_type TEXT,
|
|
233
|
+
supported_models TEXT,
|
|
234
|
+
model_limits TEXT,
|
|
235
|
+
created_at INTEGER NOT NULL,
|
|
236
|
+
updated_at INTEGER NOT NULL,
|
|
237
|
+
FOREIGN KEY (vendor_id) REFERENCES vendors(id) ON DELETE CASCADE
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
INSERT INTO api_services_new (id, vendor_id, name, api_url, api_key, source_type, supported_models, model_limits, created_at, updated_at)
|
|
241
|
+
SELECT id, vendor_id, name, api_url, api_key, source_type, supported_models, model_limits, created_at, updated_at
|
|
242
|
+
FROM api_services;
|
|
243
|
+
|
|
244
|
+
DROP TABLE api_services;
|
|
245
|
+
ALTER TABLE api_services_new RENAME TO api_services;
|
|
246
|
+
`);
|
|
247
|
+
this.db.pragma('foreign_keys = ON');
|
|
248
|
+
});
|
|
249
|
+
}
|
|
141
250
|
createTables() {
|
|
142
251
|
this.db.exec(`
|
|
143
252
|
CREATE TABLE IF NOT EXISTS vendors (
|
|
144
253
|
id TEXT PRIMARY KEY,
|
|
145
254
|
name TEXT NOT NULL,
|
|
146
255
|
description TEXT,
|
|
256
|
+
sort_order INTEGER DEFAULT 0,
|
|
147
257
|
created_at INTEGER NOT NULL,
|
|
148
258
|
updated_at INTEGER NOT NULL
|
|
149
259
|
);
|
|
@@ -154,7 +264,6 @@ class DatabaseManager {
|
|
|
154
264
|
name TEXT NOT NULL,
|
|
155
265
|
api_url TEXT NOT NULL,
|
|
156
266
|
api_key TEXT NOT NULL,
|
|
157
|
-
timeout INTEGER,
|
|
158
267
|
source_type TEXT,
|
|
159
268
|
supported_models TEXT,
|
|
160
269
|
created_at INTEGER NOT NULL,
|
|
@@ -180,6 +289,11 @@ class DatabaseManager {
|
|
|
180
289
|
target_model TEXT,
|
|
181
290
|
replaced_model TEXT,
|
|
182
291
|
sort_order INTEGER DEFAULT 0,
|
|
292
|
+
timeout INTEGER,
|
|
293
|
+
token_limit INTEGER,
|
|
294
|
+
total_tokens_used INTEGER DEFAULT 0,
|
|
295
|
+
reset_interval INTEGER,
|
|
296
|
+
last_reset_at INTEGER,
|
|
183
297
|
created_at INTEGER NOT NULL,
|
|
184
298
|
updated_at INTEGER NOT NULL,
|
|
185
299
|
FOREIGN KEY (route_id) REFERENCES routes(id) ON DELETE CASCADE,
|
|
@@ -199,9 +313,13 @@ class DatabaseManager {
|
|
|
199
313
|
const defaultConfig = {
|
|
200
314
|
enableLogging: true,
|
|
201
315
|
logRetentionDays: 30,
|
|
202
|
-
maxLogSize:
|
|
316
|
+
maxLogSize: 100000,
|
|
203
317
|
apiKey: '',
|
|
204
318
|
enableFailover: true, // 默认启用智能故障切换
|
|
319
|
+
proxyEnabled: false, // 默认不启用代理
|
|
320
|
+
proxyUrl: '',
|
|
321
|
+
proxyUsername: '',
|
|
322
|
+
proxyPassword: '',
|
|
205
323
|
};
|
|
206
324
|
this.db.prepare('INSERT INTO config (key, value) VALUES (?, ?)').run('app_config', JSON.stringify(defaultConfig));
|
|
207
325
|
}
|
|
@@ -209,11 +327,12 @@ class DatabaseManager {
|
|
|
209
327
|
}
|
|
210
328
|
// Vendor operations
|
|
211
329
|
getVendors() {
|
|
212
|
-
const rows = this.db.prepare('SELECT * FROM vendors ORDER BY created_at DESC').all();
|
|
330
|
+
const rows = this.db.prepare('SELECT * FROM vendors ORDER BY sort_order DESC, created_at DESC').all();
|
|
213
331
|
return rows.map((row) => ({
|
|
214
332
|
id: row.id,
|
|
215
333
|
name: row.name,
|
|
216
334
|
description: row.description,
|
|
335
|
+
sortOrder: row.sort_order,
|
|
217
336
|
createdAt: row.created_at,
|
|
218
337
|
updatedAt: row.updated_at,
|
|
219
338
|
}));
|
|
@@ -222,15 +341,15 @@ class DatabaseManager {
|
|
|
222
341
|
const id = crypto_1.default.randomUUID();
|
|
223
342
|
const now = Date.now();
|
|
224
343
|
this.db
|
|
225
|
-
.prepare('INSERT INTO vendors (id, name, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?)')
|
|
226
|
-
.run(id, vendor.name, vendor.description || null, now, now);
|
|
344
|
+
.prepare('INSERT INTO vendors (id, name, description, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)')
|
|
345
|
+
.run(id, vendor.name, vendor.description || null, vendor.sortOrder || 0, now, now);
|
|
227
346
|
return Object.assign(Object.assign({}, vendor), { id, createdAt: now, updatedAt: now });
|
|
228
347
|
}
|
|
229
348
|
updateVendor(id, vendor) {
|
|
230
349
|
const now = Date.now();
|
|
231
350
|
const result = this.db
|
|
232
|
-
.prepare('UPDATE vendors SET name = ?, description = ?, updated_at = ? WHERE id = ?')
|
|
233
|
-
.run(vendor.name, vendor.description || null, now, id);
|
|
351
|
+
.prepare('UPDATE vendors SET name = ?, description = ?, sort_order = ?, updated_at = ? WHERE id = ?')
|
|
352
|
+
.run(vendor.name, vendor.description || null, vendor.sortOrder !== undefined ? vendor.sortOrder : 0, now, id);
|
|
234
353
|
return result.changes > 0;
|
|
235
354
|
}
|
|
236
355
|
deleteVendor(id) {
|
|
@@ -252,10 +371,10 @@ class DatabaseManager {
|
|
|
252
371
|
name: row.name,
|
|
253
372
|
apiUrl: row.api_url,
|
|
254
373
|
apiKey: row.api_key,
|
|
255
|
-
timeout: row.timeout,
|
|
256
374
|
sourceType: row.source_type,
|
|
257
375
|
supportedModels: row.supported_models ? row.supported_models.split(',').map((model) => model.trim()).filter((model) => model.length > 0) : undefined,
|
|
258
376
|
modelLimits: row.model_limits ? JSON.parse(row.model_limits) : undefined,
|
|
377
|
+
enableProxy: row.enable_proxy === 1,
|
|
259
378
|
createdAt: row.created_at,
|
|
260
379
|
updatedAt: row.updated_at,
|
|
261
380
|
}));
|
|
@@ -269,18 +388,18 @@ class DatabaseManager {
|
|
|
269
388
|
const id = crypto_1.default.randomUUID();
|
|
270
389
|
const now = Date.now();
|
|
271
390
|
this.db
|
|
272
|
-
.prepare('INSERT INTO api_services (id, vendor_id, name, api_url, api_key,
|
|
273
|
-
.run(id, service.vendorId, service.name, service.apiUrl, service.apiKey, service.
|
|
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);
|
|
274
393
|
return Object.assign(Object.assign({}, service), { id, createdAt: now, updatedAt: now });
|
|
275
394
|
}
|
|
276
395
|
updateAPIService(id, service) {
|
|
277
396
|
const now = Date.now();
|
|
278
397
|
const result = this.db
|
|
279
|
-
.prepare('UPDATE api_services SET name = ?, api_url = ?, api_key = ?,
|
|
280
|
-
.run(service.name, service.apiUrl, service.apiKey, service.
|
|
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);
|
|
281
400
|
// 调试日志: 记录更新操作
|
|
282
401
|
if (result.changes > 0 && process.env.NODE_ENV === 'development') {
|
|
283
|
-
console.log(`[DB] Updated service ${id}: ${service.name} -> ${service.apiUrl}
|
|
402
|
+
console.log(`[DB] Updated service ${id}: ${service.name} -> ${service.apiUrl}`);
|
|
284
403
|
}
|
|
285
404
|
return result.changes > 0;
|
|
286
405
|
}
|
|
@@ -348,6 +467,11 @@ class DatabaseManager {
|
|
|
348
467
|
targetModel: row.target_model,
|
|
349
468
|
replacedModel: row.replaced_model,
|
|
350
469
|
sortOrder: row.sort_order,
|
|
470
|
+
timeout: row.timeout,
|
|
471
|
+
tokenLimit: row.token_limit,
|
|
472
|
+
totalTokensUsed: row.total_tokens_used,
|
|
473
|
+
resetInterval: row.reset_interval,
|
|
474
|
+
lastResetAt: row.last_reset_at,
|
|
351
475
|
createdAt: row.created_at,
|
|
352
476
|
updatedAt: row.updated_at,
|
|
353
477
|
}));
|
|
@@ -356,26 +480,79 @@ class DatabaseManager {
|
|
|
356
480
|
const id = crypto_1.default.randomUUID();
|
|
357
481
|
const now = Date.now();
|
|
358
482
|
this.db
|
|
359
|
-
.prepare('INSERT INTO rules (id, route_id, content_type, target_service_id, target_model, replaced_model, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
|
360
|
-
.run(id, route.routeId, route.contentType, route.targetServiceId, route.targetModel || null, route.replacedModel || null, route.sortOrder || 0, now, now);
|
|
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);
|
|
361
485
|
return Object.assign(Object.assign({}, route), { id, createdAt: now, updatedAt: now });
|
|
362
486
|
}
|
|
363
487
|
updateRule(id, route) {
|
|
364
488
|
const now = Date.now();
|
|
365
489
|
const result = this.db
|
|
366
|
-
.prepare('UPDATE rules SET content_type = ?, target_service_id = ?, target_model = ?, replaced_model = ?, sort_order = ?, updated_at = ? WHERE id = ?')
|
|
367
|
-
.run(route.contentType, route.targetServiceId, route.targetModel || null, route.replacedModel || null, route.sortOrder || 0, now, id);
|
|
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);
|
|
368
492
|
return result.changes > 0;
|
|
369
493
|
}
|
|
370
494
|
deleteRule(id) {
|
|
371
495
|
const result = this.db.prepare('DELETE FROM rules WHERE id = ?').run(id);
|
|
372
496
|
return result.changes > 0;
|
|
373
497
|
}
|
|
498
|
+
/**
|
|
499
|
+
* 增加规则的token使用量
|
|
500
|
+
* @param ruleId 规则ID
|
|
501
|
+
* @param tokensUsed 使用的token数量
|
|
502
|
+
* @returns 是否成功
|
|
503
|
+
*/
|
|
504
|
+
incrementRuleTokenUsage(ruleId, tokensUsed) {
|
|
505
|
+
const result = this.db
|
|
506
|
+
.prepare('UPDATE rules SET total_tokens_used = total_tokens_used + ? WHERE id = ?')
|
|
507
|
+
.run(tokensUsed, ruleId);
|
|
508
|
+
return result.changes > 0;
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* 重置规则的token使用量
|
|
512
|
+
* @param ruleId 规则ID
|
|
513
|
+
* @returns 是否成功
|
|
514
|
+
*/
|
|
515
|
+
resetRuleTokenUsage(ruleId) {
|
|
516
|
+
const now = Date.now();
|
|
517
|
+
const result = this.db
|
|
518
|
+
.prepare('UPDATE rules SET total_tokens_used = 0, last_reset_at = ? WHERE id = ?')
|
|
519
|
+
.run(now, ruleId);
|
|
520
|
+
return result.changes > 0;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* 检查并重置到期的规则
|
|
524
|
+
* 如果规则设置了reset_interval且已经到了重置时间,则自动重置token使用量
|
|
525
|
+
* @param ruleId 规则ID
|
|
526
|
+
* @returns 是否进行了重置
|
|
527
|
+
*/
|
|
528
|
+
checkAndResetRuleIfNeeded(ruleId) {
|
|
529
|
+
const rule = this.db
|
|
530
|
+
.prepare('SELECT reset_interval, last_reset_at FROM rules WHERE id = ?')
|
|
531
|
+
.get(ruleId);
|
|
532
|
+
if (!rule || !rule.reset_interval) {
|
|
533
|
+
return false; // 没有设置重置间隔
|
|
534
|
+
}
|
|
535
|
+
const now = Date.now();
|
|
536
|
+
const resetIntervalMs = rule.reset_interval * 60 * 60 * 1000; // 小时转毫秒
|
|
537
|
+
const lastResetAt = rule.last_reset_at || 0;
|
|
538
|
+
// 检查是否已经到了重置时间
|
|
539
|
+
if (now - lastResetAt >= resetIntervalMs) {
|
|
540
|
+
this.resetRuleTokenUsage(ruleId);
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
374
545
|
// Log operations
|
|
375
546
|
addLog(log) {
|
|
376
547
|
return __awaiter(this, void 0, void 0, function* () {
|
|
548
|
+
const { path } = log;
|
|
549
|
+
if (!path.startsWith('/v1/')) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
377
552
|
const id = crypto_1.default.randomUUID();
|
|
378
553
|
yield this.logDb.put(id, JSON.stringify(Object.assign(Object.assign({}, log), { id })));
|
|
554
|
+
// 清除缓存
|
|
555
|
+
this.logsCountCache = null;
|
|
379
556
|
});
|
|
380
557
|
}
|
|
381
558
|
getLogs() {
|
|
@@ -406,6 +583,8 @@ class DatabaseManager {
|
|
|
406
583
|
clearLogs() {
|
|
407
584
|
return __awaiter(this, void 0, void 0, function* () {
|
|
408
585
|
yield this.logDb.clear();
|
|
586
|
+
// 清除缓存
|
|
587
|
+
this.logsCountCache = null;
|
|
409
588
|
});
|
|
410
589
|
}
|
|
411
590
|
// Access log operations
|
|
@@ -413,6 +592,8 @@ class DatabaseManager {
|
|
|
413
592
|
return __awaiter(this, void 0, void 0, function* () {
|
|
414
593
|
const id = crypto_1.default.randomUUID();
|
|
415
594
|
yield this.accessLogDb.put(id, JSON.stringify(Object.assign(Object.assign({}, log), { id })));
|
|
595
|
+
// 清除缓存
|
|
596
|
+
this.accessLogsCountCache = null;
|
|
416
597
|
return id;
|
|
417
598
|
});
|
|
418
599
|
}
|
|
@@ -451,6 +632,8 @@ class DatabaseManager {
|
|
|
451
632
|
clearAccessLogs() {
|
|
452
633
|
return __awaiter(this, void 0, void 0, function* () {
|
|
453
634
|
yield this.accessLogDb.clear();
|
|
635
|
+
// 清除缓存
|
|
636
|
+
this.accessLogsCountCache = null;
|
|
454
637
|
});
|
|
455
638
|
}
|
|
456
639
|
// Error log operations
|
|
@@ -458,6 +641,8 @@ class DatabaseManager {
|
|
|
458
641
|
return __awaiter(this, void 0, void 0, function* () {
|
|
459
642
|
const id = crypto_1.default.randomUUID();
|
|
460
643
|
yield this.errorLogDb.put(id, JSON.stringify(Object.assign(Object.assign({}, log), { id })));
|
|
644
|
+
// 清除缓存
|
|
645
|
+
this.errorLogsCountCache = null;
|
|
461
646
|
});
|
|
462
647
|
}
|
|
463
648
|
getErrorLogs() {
|
|
@@ -488,6 +673,98 @@ class DatabaseManager {
|
|
|
488
673
|
clearErrorLogs() {
|
|
489
674
|
return __awaiter(this, void 0, void 0, function* () {
|
|
490
675
|
yield this.errorLogDb.clear();
|
|
676
|
+
// 清除缓存
|
|
677
|
+
this.errorLogsCountCache = null;
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* 获取请求日志总数(带缓存)
|
|
682
|
+
*/
|
|
683
|
+
getLogsCount() {
|
|
684
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
685
|
+
var _a, e_4, _b, _c;
|
|
686
|
+
const now = Date.now();
|
|
687
|
+
if (this.logsCountCache && now - this.logsCountCache.timestamp < this.CACHE_TTL) {
|
|
688
|
+
return this.logsCountCache.count;
|
|
689
|
+
}
|
|
690
|
+
let count = 0;
|
|
691
|
+
try {
|
|
692
|
+
for (var _d = true, _e = __asyncValues(this.logDb.iterator()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
693
|
+
_c = _f.value;
|
|
694
|
+
_d = false;
|
|
695
|
+
const _ = _c;
|
|
696
|
+
count++;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
700
|
+
finally {
|
|
701
|
+
try {
|
|
702
|
+
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
703
|
+
}
|
|
704
|
+
finally { if (e_4) throw e_4.error; }
|
|
705
|
+
}
|
|
706
|
+
this.logsCountCache = { count, timestamp: now };
|
|
707
|
+
return count;
|
|
708
|
+
});
|
|
709
|
+
}
|
|
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
|
+
/**
|
|
741
|
+
* 获取错误日志总数(带缓存)
|
|
742
|
+
*/
|
|
743
|
+
getErrorLogsCount() {
|
|
744
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
745
|
+
var _a, e_6, _b, _c;
|
|
746
|
+
const now = Date.now();
|
|
747
|
+
if (this.errorLogsCountCache && now - this.errorLogsCountCache.timestamp < this.CACHE_TTL) {
|
|
748
|
+
return this.errorLogsCountCache.count;
|
|
749
|
+
}
|
|
750
|
+
let count = 0;
|
|
751
|
+
try {
|
|
752
|
+
for (var _d = true, _e = __asyncValues(this.errorLogDb.iterator()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
753
|
+
_c = _f.value;
|
|
754
|
+
_d = false;
|
|
755
|
+
const _ = _c;
|
|
756
|
+
count++;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
|
760
|
+
finally {
|
|
761
|
+
try {
|
|
762
|
+
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
763
|
+
}
|
|
764
|
+
finally { if (e_6) throw e_6.error; }
|
|
765
|
+
}
|
|
766
|
+
this.errorLogsCountCache = { count, timestamp: now };
|
|
767
|
+
return count;
|
|
491
768
|
});
|
|
492
769
|
}
|
|
493
770
|
// Service blacklist operations
|
|
@@ -552,7 +829,7 @@ class DatabaseManager {
|
|
|
552
829
|
}
|
|
553
830
|
cleanupExpiredBlacklist() {
|
|
554
831
|
return __awaiter(this, void 0, void 0, function* () {
|
|
555
|
-
var _a,
|
|
832
|
+
var _a, e_7, _b, _c;
|
|
556
833
|
const now = Date.now();
|
|
557
834
|
let count = 0;
|
|
558
835
|
try {
|
|
@@ -567,12 +844,12 @@ class DatabaseManager {
|
|
|
567
844
|
}
|
|
568
845
|
}
|
|
569
846
|
}
|
|
570
|
-
catch (
|
|
847
|
+
catch (e_7_1) { e_7 = { error: e_7_1 }; }
|
|
571
848
|
finally {
|
|
572
849
|
try {
|
|
573
850
|
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
|
574
851
|
}
|
|
575
|
-
finally { if (
|
|
852
|
+
finally { if (e_7) throw e_7.error; }
|
|
576
853
|
}
|
|
577
854
|
return count;
|
|
578
855
|
});
|
|
@@ -619,14 +896,14 @@ class DatabaseManager {
|
|
|
619
896
|
// Import vendors
|
|
620
897
|
for (const vendor of importData.vendors) {
|
|
621
898
|
this.db
|
|
622
|
-
.prepare('INSERT INTO vendors (id, name, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?)')
|
|
623
|
-
.run(vendor.id, vendor.name, vendor.description || null, vendor.createdAt, vendor.updatedAt);
|
|
899
|
+
.prepare('INSERT INTO vendors (id, name, description, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)')
|
|
900
|
+
.run(vendor.id, vendor.name, vendor.description || null, vendor.sortOrder || 0, vendor.createdAt, vendor.updatedAt);
|
|
624
901
|
}
|
|
625
902
|
// Import API services
|
|
626
903
|
for (const service of importData.apiServices) {
|
|
627
904
|
this.db
|
|
628
|
-
.prepare('INSERT INTO api_services (id, vendor_id, name, api_url, api_key,
|
|
629
|
-
.run(service.id, service.vendorId, service.name, service.apiUrl, service.apiKey, service.
|
|
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);
|
|
630
907
|
}
|
|
631
908
|
// Import routes
|
|
632
909
|
for (const route of importData.routes) {
|
|
@@ -637,8 +914,8 @@ class DatabaseManager {
|
|
|
637
914
|
// Import rules
|
|
638
915
|
for (const rule of importData.rules) {
|
|
639
916
|
this.db
|
|
640
|
-
.prepare('INSERT INTO rules (id, route_id, content_type, target_service_id, target_model, replaced_model, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
|
641
|
-
.run(rule.id, rule.routeId, rule.contentType || 'default', rule.targetServiceId, rule.targetModel || null, rule.replacedModel || null, rule.sortOrder || 0, rule.createdAt, rule.updatedAt);
|
|
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);
|
|
642
919
|
}
|
|
643
920
|
// Update config
|
|
644
921
|
this.updateConfig(importData.config);
|
|
@@ -653,7 +930,7 @@ class DatabaseManager {
|
|
|
653
930
|
// Statistics operations
|
|
654
931
|
getStatistics() {
|
|
655
932
|
return __awaiter(this, arguments, void 0, function* (days = 30) {
|
|
656
|
-
var _a,
|
|
933
|
+
var _a, e_8, _b, _c, _d, e_9, _e, _f;
|
|
657
934
|
var _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
658
935
|
const now = Date.now();
|
|
659
936
|
const startTime = now - days * 24 * 60 * 60 * 1000;
|
|
@@ -670,12 +947,12 @@ class DatabaseManager {
|
|
|
670
947
|
}
|
|
671
948
|
}
|
|
672
949
|
}
|
|
673
|
-
catch (
|
|
950
|
+
catch (e_8_1) { e_8 = { error: e_8_1 }; }
|
|
674
951
|
finally {
|
|
675
952
|
try {
|
|
676
953
|
if (!_z && !_a && (_b = _0.return)) yield _b.call(_0);
|
|
677
954
|
}
|
|
678
|
-
finally { if (
|
|
955
|
+
finally { if (e_8) throw e_8.error; }
|
|
679
956
|
}
|
|
680
957
|
// Get all error logs
|
|
681
958
|
const errorLogs = [];
|
|
@@ -693,12 +970,12 @@ class DatabaseManager {
|
|
|
693
970
|
}
|
|
694
971
|
}
|
|
695
972
|
}
|
|
696
|
-
catch (
|
|
973
|
+
catch (e_9_1) { e_9 = { error: e_9_1 }; }
|
|
697
974
|
finally {
|
|
698
975
|
try {
|
|
699
976
|
if (!_2 && !_d && (_e = _3.return)) yield _e.call(_3);
|
|
700
977
|
}
|
|
701
|
-
finally { if (
|
|
978
|
+
finally { if (e_9) throw e_9.error; }
|
|
702
979
|
}
|
|
703
980
|
// Get vendors and services for mapping
|
|
704
981
|
const vendors = this.getVendors();
|
package/dist/server/main.js
CHANGED
|
@@ -282,6 +282,7 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
282
282
|
app.post('/api/rules', (req, res) => res.json(dbManager.createRule(req.body)));
|
|
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
|
+
app.put('/api/rules/:id/reset-tokens', (req, res) => res.json(dbManager.resetRuleTokenUsage(req.params.id)));
|
|
285
286
|
app.get('/api/logs', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
286
287
|
const rawLimit = typeof req.query.limit === 'string' ? parseInt(req.query.limit, 10) : NaN;
|
|
287
288
|
const rawOffset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : NaN;
|
|
@@ -318,6 +319,18 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
318
319
|
yield dbManager.clearErrorLogs();
|
|
319
320
|
res.json(true);
|
|
320
321
|
})));
|
|
322
|
+
app.get('/api/logs/count', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
323
|
+
const count = yield dbManager.getLogsCount();
|
|
324
|
+
res.json({ count });
|
|
325
|
+
})));
|
|
326
|
+
app.get('/api/access-logs/count', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
327
|
+
const count = yield dbManager.getAccessLogsCount();
|
|
328
|
+
res.json({ count });
|
|
329
|
+
})));
|
|
330
|
+
app.get('/api/error-logs/count', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
331
|
+
const count = yield dbManager.getErrorLogsCount();
|
|
332
|
+
res.json({ count });
|
|
333
|
+
})));
|
|
321
334
|
app.get('/api/config', (_req, res) => res.json(dbManager.getConfig()));
|
|
322
335
|
app.put('/api/config', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
323
336
|
const config = req.body;
|