aicodeswitch 4.0.0 → 4.0.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/dist/server/auth.js +12 -6
- package/dist/server/fs-database.js +240 -6
- package/dist/server/main.js +28 -10
- package/dist/server/proxy-server.js +126 -63
- package/dist/server/rules-status-service.js +20 -137
- package/dist/server/transformers/chunk-collector.js +38 -5
- package/dist/server/transformers/streaming.js +47 -29
- package/dist/server/transformers/transformers.js +20 -18
- package/dist/server/version-check.js +3 -2
- package/dist/ui/assets/index-BRHavnvn.js +514 -0
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
- package/dist/ui/assets/index-GQBwe1Rm.js +0 -514
package/dist/server/auth.js
CHANGED
|
@@ -10,14 +10,20 @@ exports.verifyToken = verifyToken;
|
|
|
10
10
|
exports.authMiddleware = authMiddleware;
|
|
11
11
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
12
12
|
const crypto_1 = __importDefault(require("crypto"));
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
// 延迟读取 process.env.AUTH,避免模块加载时 dotenv 尚未执行导致值始终为空
|
|
14
|
+
function getAuthCode() {
|
|
15
|
+
return process.env.AUTH || '';
|
|
16
|
+
}
|
|
17
|
+
function getJwtSecret() {
|
|
18
|
+
const authCode = getAuthCode();
|
|
19
|
+
return process.env.JWT_SECRET || (authCode ? crypto_1.default.createHash('sha256').update(authCode).digest('hex') : '');
|
|
20
|
+
}
|
|
15
21
|
const TOKEN_EXPIRY = '7d'; // 7天有效期
|
|
16
22
|
/**
|
|
17
23
|
* 检查是否启用鉴权
|
|
18
24
|
*/
|
|
19
25
|
function isAuthEnabled() {
|
|
20
|
-
return
|
|
26
|
+
return getAuthCode().trim().length > 0;
|
|
21
27
|
}
|
|
22
28
|
/**
|
|
23
29
|
* 验证鉴权码
|
|
@@ -26,7 +32,7 @@ function verifyAuthCode(authCode) {
|
|
|
26
32
|
if (!isAuthEnabled()) {
|
|
27
33
|
return true; // 未启用鉴权,直接通过
|
|
28
34
|
}
|
|
29
|
-
return authCode ===
|
|
35
|
+
return authCode === getAuthCode();
|
|
30
36
|
}
|
|
31
37
|
/**
|
|
32
38
|
* 生成 JWT Token
|
|
@@ -35,14 +41,14 @@ function generateToken() {
|
|
|
35
41
|
const payload = {
|
|
36
42
|
authenticated: true,
|
|
37
43
|
};
|
|
38
|
-
return jsonwebtoken_1.default.sign(payload,
|
|
44
|
+
return jsonwebtoken_1.default.sign(payload, getJwtSecret(), { expiresIn: TOKEN_EXPIRY });
|
|
39
45
|
}
|
|
40
46
|
/**
|
|
41
47
|
* 验证 JWT Token
|
|
42
48
|
*/
|
|
43
49
|
function verifyToken(token) {
|
|
44
50
|
try {
|
|
45
|
-
jsonwebtoken_1.default.verify(token,
|
|
51
|
+
jsonwebtoken_1.default.verify(token, getJwtSecret());
|
|
46
52
|
return true;
|
|
47
53
|
}
|
|
48
54
|
catch (error) {
|
|
@@ -56,6 +56,7 @@ class FileSystemDatabaseManager {
|
|
|
56
56
|
get sessionsFile() { return path_1.default.join(this.dataPath, 'sessions.json'); }
|
|
57
57
|
get logsDir() { return path_1.default.join(this.dataPath, 'logs'); }
|
|
58
58
|
get logsIndexFile() { return path_1.default.join(this.dataPath, 'logs-index.json'); }
|
|
59
|
+
get sessionLogIndexFile() { return path_1.default.join(this.dataPath, 'session-log-index.json'); }
|
|
59
60
|
get errorLogsFile() { return path_1.default.join(this.dataPath, 'error-logs.json'); }
|
|
60
61
|
get blacklistFile() { return path_1.default.join(this.dataPath, 'blacklist.json'); }
|
|
61
62
|
get statisticsFile() { return path_1.default.join(this.dataPath, 'statistics.json'); }
|
|
@@ -133,6 +134,42 @@ class FileSystemDatabaseManager {
|
|
|
133
134
|
writable: true,
|
|
134
135
|
value: []
|
|
135
136
|
});
|
|
137
|
+
Object.defineProperty(this, "sessionLogIndex", {
|
|
138
|
+
enumerable: true,
|
|
139
|
+
configurable: true,
|
|
140
|
+
writable: true,
|
|
141
|
+
value: new Map()
|
|
142
|
+
});
|
|
143
|
+
Object.defineProperty(this, "sessionLogIndexDirty", {
|
|
144
|
+
enumerable: true,
|
|
145
|
+
configurable: true,
|
|
146
|
+
writable: true,
|
|
147
|
+
value: false
|
|
148
|
+
});
|
|
149
|
+
Object.defineProperty(this, "sessionLogIndexDirtyCount", {
|
|
150
|
+
enumerable: true,
|
|
151
|
+
configurable: true,
|
|
152
|
+
writable: true,
|
|
153
|
+
value: 0
|
|
154
|
+
});
|
|
155
|
+
Object.defineProperty(this, "sessionLogIndexFlushTimer", {
|
|
156
|
+
enumerable: true,
|
|
157
|
+
configurable: true,
|
|
158
|
+
writable: true,
|
|
159
|
+
value: null
|
|
160
|
+
});
|
|
161
|
+
Object.defineProperty(this, "SESSION_LOG_INDEX_FLUSH_DELAY", {
|
|
162
|
+
enumerable: true,
|
|
163
|
+
configurable: true,
|
|
164
|
+
writable: true,
|
|
165
|
+
value: 3000
|
|
166
|
+
});
|
|
167
|
+
Object.defineProperty(this, "SESSION_LOG_INDEX_FLUSH_THRESHOLD", {
|
|
168
|
+
enumerable: true,
|
|
169
|
+
configurable: true,
|
|
170
|
+
writable: true,
|
|
171
|
+
value: 50
|
|
172
|
+
});
|
|
136
173
|
Object.defineProperty(this, "errorLogs", {
|
|
137
174
|
enumerable: true,
|
|
138
175
|
configurable: true,
|
|
@@ -242,6 +279,8 @@ class FileSystemDatabaseManager {
|
|
|
242
279
|
this.loadStatistics(),
|
|
243
280
|
this.loadMCPs(),
|
|
244
281
|
]);
|
|
282
|
+
// 会话日志索引依赖 logShardsIndex,必须在 loadLogsIndex 之后
|
|
283
|
+
yield this.loadSessionLogIndex();
|
|
245
284
|
});
|
|
246
285
|
}
|
|
247
286
|
loadVendors() {
|
|
@@ -571,6 +610,96 @@ class FileSystemDatabaseManager {
|
|
|
571
610
|
yield promises_1.default.writeFile(this.logsIndexFile, JSON.stringify(this.logShardsIndex, null, 2));
|
|
572
611
|
});
|
|
573
612
|
}
|
|
613
|
+
/**
|
|
614
|
+
* 加载会话日志索引,若不存在则从现有日志全量构建
|
|
615
|
+
*/
|
|
616
|
+
loadSessionLogIndex() {
|
|
617
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
618
|
+
try {
|
|
619
|
+
const data = yield promises_1.default.readFile(this.sessionLogIndexFile, 'utf-8');
|
|
620
|
+
const parsed = JSON.parse(data);
|
|
621
|
+
this.sessionLogIndex = new Map(Object.entries(parsed));
|
|
622
|
+
console.log(`[Database] Session log index loaded: ${this.sessionLogIndex.size} sessions`);
|
|
623
|
+
}
|
|
624
|
+
catch (_a) {
|
|
625
|
+
// 索引文件不存在,从现有日志全量构建
|
|
626
|
+
console.log('[Database] Session log index not found, building from existing logs...');
|
|
627
|
+
yield this.buildSessionLogIndex();
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* 从所有现有日志分片全量构建会话日志索引(首次启动或迁移用)
|
|
633
|
+
*/
|
|
634
|
+
buildSessionLogIndex() {
|
|
635
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
636
|
+
this.sessionLogIndex.clear();
|
|
637
|
+
for (const shard of this.logShardsIndex) {
|
|
638
|
+
const shardLogs = yield this.loadLogShard(shard.filename);
|
|
639
|
+
for (let i = 0; i < shardLogs.length; i++) {
|
|
640
|
+
const sessionId = this.extractSessionIdFromLog(shardLogs[i]);
|
|
641
|
+
if (sessionId) {
|
|
642
|
+
let refs = this.sessionLogIndex.get(sessionId);
|
|
643
|
+
if (!refs) {
|
|
644
|
+
refs = [];
|
|
645
|
+
this.sessionLogIndex.set(sessionId, refs);
|
|
646
|
+
}
|
|
647
|
+
refs.push({ filename: shard.filename, index: i, timestamp: shardLogs[i].timestamp });
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (this.sessionLogIndex.size > 0) {
|
|
652
|
+
yield this.saveSessionLogIndexNow();
|
|
653
|
+
console.log(`[Database] Session log index built: ${this.sessionLogIndex.size} sessions indexed`);
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* 将会话日志索引写入磁盘
|
|
659
|
+
*/
|
|
660
|
+
saveSessionLogIndexNow() {
|
|
661
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
662
|
+
const obj = {};
|
|
663
|
+
for (const [key, refs] of this.sessionLogIndex) {
|
|
664
|
+
obj[key] = refs;
|
|
665
|
+
}
|
|
666
|
+
yield promises_1.default.writeFile(this.sessionLogIndexFile, JSON.stringify(obj));
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* 标记索引脏数据,触发防抖写盘
|
|
671
|
+
*/
|
|
672
|
+
scheduleSessionLogIndexFlush() {
|
|
673
|
+
this.sessionLogIndexDirty = true;
|
|
674
|
+
this.sessionLogIndexDirtyCount++;
|
|
675
|
+
// 达到阈值立即刷盘
|
|
676
|
+
if (this.sessionLogIndexDirtyCount >= this.SESSION_LOG_INDEX_FLUSH_THRESHOLD) {
|
|
677
|
+
this.flushSessionLogIndex();
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
// 防抖定时器
|
|
681
|
+
if (!this.sessionLogIndexFlushTimer) {
|
|
682
|
+
this.sessionLogIndexFlushTimer = setTimeout(() => {
|
|
683
|
+
this.flushSessionLogIndex();
|
|
684
|
+
}, this.SESSION_LOG_INDEX_FLUSH_DELAY);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* 立即将索引刷盘(关闭时调用)
|
|
689
|
+
*/
|
|
690
|
+
flushSessionLogIndex() {
|
|
691
|
+
if (this.sessionLogIndexFlushTimer) {
|
|
692
|
+
clearTimeout(this.sessionLogIndexFlushTimer);
|
|
693
|
+
this.sessionLogIndexFlushTimer = null;
|
|
694
|
+
}
|
|
695
|
+
if (this.sessionLogIndexDirty) {
|
|
696
|
+
this.sessionLogIndexDirty = false;
|
|
697
|
+
this.sessionLogIndexDirtyCount = 0;
|
|
698
|
+
this.saveSessionLogIndexNow().catch(err => {
|
|
699
|
+
console.error('[Database] Failed to flush session log index:', err);
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
574
703
|
/**
|
|
575
704
|
* 迁移旧的 logs.json 文件到新的分片格式
|
|
576
705
|
*/
|
|
@@ -683,6 +812,18 @@ class FileSystemDatabaseManager {
|
|
|
683
812
|
this.logShardsIndex = this.logShardsIndex.filter(s => !toDelete.includes(s.filename));
|
|
684
813
|
if (toDelete.length > 0) {
|
|
685
814
|
yield this.saveLogsIndex();
|
|
815
|
+
// 同步清理会话日志索引中被删除分片的条目
|
|
816
|
+
const deleteSet = new Set(toDelete);
|
|
817
|
+
for (const [sid, refs] of this.sessionLogIndex) {
|
|
818
|
+
const remaining = refs.filter(r => !deleteSet.has(r.filename));
|
|
819
|
+
if (remaining.length === 0) {
|
|
820
|
+
this.sessionLogIndex.delete(sid);
|
|
821
|
+
}
|
|
822
|
+
else if (remaining.length < refs.length) {
|
|
823
|
+
this.sessionLogIndex.set(sid, remaining);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
yield this.saveSessionLogIndexNow();
|
|
686
827
|
}
|
|
687
828
|
});
|
|
688
829
|
}
|
|
@@ -1413,6 +1554,17 @@ class FileSystemDatabaseManager {
|
|
|
1413
1554
|
yield this.updateStatistics(logWithId);
|
|
1414
1555
|
// 清除计数缓存
|
|
1415
1556
|
this.logsCountCache = null;
|
|
1557
|
+
// 更新会话日志索引
|
|
1558
|
+
const sessionId = this.extractSessionIdFromLog(logWithId);
|
|
1559
|
+
if (sessionId) {
|
|
1560
|
+
let refs = this.sessionLogIndex.get(sessionId);
|
|
1561
|
+
if (!refs) {
|
|
1562
|
+
refs = [];
|
|
1563
|
+
this.sessionLogIndex.set(sessionId, refs);
|
|
1564
|
+
}
|
|
1565
|
+
refs.push({ filename, index: shardLogs.length - 1, timestamp: logWithId.timestamp });
|
|
1566
|
+
this.scheduleSessionLogIndexFlush();
|
|
1567
|
+
}
|
|
1416
1568
|
});
|
|
1417
1569
|
}
|
|
1418
1570
|
getLogs() {
|
|
@@ -2381,8 +2533,37 @@ class FileSystemDatabaseManager {
|
|
|
2381
2533
|
}
|
|
2382
2534
|
getLogsBySessionId(sessionId_1) {
|
|
2383
2535
|
return __awaiter(this, arguments, void 0, function* (sessionId, limit = 100) {
|
|
2536
|
+
const refs = this.sessionLogIndex.get(sessionId);
|
|
2537
|
+
// 有索引:仅加载相关分片,按 index 直接取值
|
|
2538
|
+
if (refs && refs.length > 0) {
|
|
2539
|
+
// 按 filename 分组,避免重复加载同一分片
|
|
2540
|
+
const shardMap = new Map();
|
|
2541
|
+
for (const ref of refs) {
|
|
2542
|
+
let indices = shardMap.get(ref.filename);
|
|
2543
|
+
if (!indices) {
|
|
2544
|
+
indices = [];
|
|
2545
|
+
shardMap.set(ref.filename, indices);
|
|
2546
|
+
}
|
|
2547
|
+
indices.push(ref.index);
|
|
2548
|
+
}
|
|
2549
|
+
const logs = [];
|
|
2550
|
+
for (const [filename, indices] of shardMap) {
|
|
2551
|
+
try {
|
|
2552
|
+
const shardLogs = yield this.loadLogShard(filename);
|
|
2553
|
+
for (const idx of indices) {
|
|
2554
|
+
if (idx >= 0 && idx < shardLogs.length) {
|
|
2555
|
+
logs.push(shardLogs[idx]);
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
catch (_a) {
|
|
2560
|
+
// 分片文件可能已被清理,跳过
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
return logs.sort((a, b) => b.timestamp - a.timestamp).slice(0, limit);
|
|
2564
|
+
}
|
|
2565
|
+
// 无索引(兼容旧数据):回退到全扫描
|
|
2384
2566
|
const allLogs = [];
|
|
2385
|
-
// 遍历所有分片
|
|
2386
2567
|
for (const shard of this.logShardsIndex) {
|
|
2387
2568
|
const shardLogs = yield this.loadLogShard(shard.filename);
|
|
2388
2569
|
const filtered = shardLogs.filter(log => this.isLogBelongsToSession(log, sessionId));
|
|
@@ -2405,22 +2586,73 @@ class FileSystemDatabaseManager {
|
|
|
2405
2586
|
try {
|
|
2406
2587
|
// body 可能是对象(已解析)或字符串(未解析)
|
|
2407
2588
|
const body = typeof log.body === 'string' ? JSON.parse(log.body) : log.body;
|
|
2408
|
-
if ((
|
|
2409
|
-
|
|
2589
|
+
if ((_b = body.metadata) === null || _b === void 0 ? void 0 : _b.user_id) {
|
|
2590
|
+
const userId = body.metadata.user_id;
|
|
2591
|
+
// 兼容新旧格式:新版本为 JSON 字符串,旧版本为纯字符串
|
|
2592
|
+
let extractedSessionId = null;
|
|
2593
|
+
try {
|
|
2594
|
+
const parsed = JSON.parse(userId);
|
|
2595
|
+
if (parsed && typeof parsed === 'object' && parsed.session_id) {
|
|
2596
|
+
extractedSessionId = parsed.session_id;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
catch (_c) {
|
|
2600
|
+
// 不是 JSON,按旧版本纯字符串处理
|
|
2601
|
+
extractedSessionId = userId;
|
|
2602
|
+
}
|
|
2603
|
+
if (extractedSessionId === sessionId) {
|
|
2604
|
+
return true;
|
|
2605
|
+
}
|
|
2410
2606
|
}
|
|
2411
2607
|
}
|
|
2412
|
-
catch (
|
|
2608
|
+
catch (_d) {
|
|
2413
2609
|
// 忽略解析错误
|
|
2414
2610
|
}
|
|
2415
2611
|
}
|
|
2416
2612
|
return false;
|
|
2417
2613
|
}
|
|
2614
|
+
/**
|
|
2615
|
+
* 从日志条目中提取 sessionId(用于索引)
|
|
2616
|
+
* Codex: headers['session_id']
|
|
2617
|
+
* Claude Code: body.metadata.user_id(兼容新旧格式)
|
|
2618
|
+
*/
|
|
2619
|
+
extractSessionIdFromLog(log) {
|
|
2620
|
+
var _a, _b;
|
|
2621
|
+
// Codex: headers 中的 session_id
|
|
2622
|
+
const headerSessionId = (_a = log.headers) === null || _a === void 0 ? void 0 : _a['session_id'];
|
|
2623
|
+
if (typeof headerSessionId === 'string')
|
|
2624
|
+
return headerSessionId;
|
|
2625
|
+
// Claude Code: body 中的 metadata.user_id
|
|
2626
|
+
if (log.body) {
|
|
2627
|
+
try {
|
|
2628
|
+
const body = typeof log.body === 'string' ? JSON.parse(log.body) : log.body;
|
|
2629
|
+
if ((_b = body.metadata) === null || _b === void 0 ? void 0 : _b.user_id) {
|
|
2630
|
+
const userId = body.metadata.user_id;
|
|
2631
|
+
try {
|
|
2632
|
+
const parsed = JSON.parse(userId);
|
|
2633
|
+
if (parsed && typeof parsed === 'object' && parsed.session_id) {
|
|
2634
|
+
return parsed.session_id;
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
catch (_c) {
|
|
2638
|
+
return userId;
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
catch (_d) {
|
|
2643
|
+
// 忽略解析错误
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
return null;
|
|
2647
|
+
}
|
|
2418
2648
|
deleteSession(sessionId) {
|
|
2419
2649
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2420
2650
|
const index = this.sessions.findIndex(s => s.id === sessionId);
|
|
2421
2651
|
if (index === -1)
|
|
2422
2652
|
return false;
|
|
2423
2653
|
this.sessions.splice(index, 1);
|
|
2654
|
+
this.sessionLogIndex.delete(sessionId);
|
|
2655
|
+
this.scheduleSessionLogIndexFlush();
|
|
2424
2656
|
yield this.saveSessions();
|
|
2425
2657
|
return true;
|
|
2426
2658
|
});
|
|
@@ -2428,6 +2660,8 @@ class FileSystemDatabaseManager {
|
|
|
2428
2660
|
clearSessions() {
|
|
2429
2661
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2430
2662
|
this.sessions = [];
|
|
2663
|
+
this.sessionLogIndex.clear();
|
|
2664
|
+
this.scheduleSessionLogIndexFlush();
|
|
2431
2665
|
yield this.saveSessions();
|
|
2432
2666
|
});
|
|
2433
2667
|
}
|
|
@@ -2542,8 +2776,8 @@ class FileSystemDatabaseManager {
|
|
|
2542
2776
|
}
|
|
2543
2777
|
// Close method for compatibility (no-op for filesystem database)
|
|
2544
2778
|
close() {
|
|
2545
|
-
//
|
|
2546
|
-
|
|
2779
|
+
// 刷盘会话日志索引
|
|
2780
|
+
this.flushSessionLogIndex();
|
|
2547
2781
|
}
|
|
2548
2782
|
}
|
|
2549
2783
|
exports.FileSystemDatabaseManager = FileSystemDatabaseManager;
|
package/dist/server/main.js
CHANGED
|
@@ -1082,6 +1082,32 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
1082
1082
|
res.status(500).json({ error: 'Failed to clear blacklist' });
|
|
1083
1083
|
}
|
|
1084
1084
|
})));
|
|
1085
|
+
// 获取所有规则的当前状态
|
|
1086
|
+
app.get('/api/rules/status', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1087
|
+
// 获取所有规则
|
|
1088
|
+
const allRules = dbManager.getRules();
|
|
1089
|
+
// 获取有状态记录的规则
|
|
1090
|
+
const statusMap = rules_status_service_1.rulesStatusBroadcaster.getAllRuleStatuses();
|
|
1091
|
+
// 将数组转换为 Map 以便快速查找
|
|
1092
|
+
const statusMapByRuleId = new Map(statusMap.map(status => [status.ruleId, status]));
|
|
1093
|
+
// 合并所有规则的状态
|
|
1094
|
+
const allStatuses = allRules.map(rule => {
|
|
1095
|
+
const existingStatus = statusMapByRuleId.get(rule.id);
|
|
1096
|
+
if (existingStatus) {
|
|
1097
|
+
// 如果有状态记录,返回记录的状态
|
|
1098
|
+
return existingStatus;
|
|
1099
|
+
}
|
|
1100
|
+
else {
|
|
1101
|
+
// 如果没有状态记录,返回默认的 idle 状态
|
|
1102
|
+
return {
|
|
1103
|
+
ruleId: rule.id,
|
|
1104
|
+
status: 'idle',
|
|
1105
|
+
timestamp: Date.now(),
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
res.json(allStatuses);
|
|
1110
|
+
})));
|
|
1085
1111
|
// 清除规则的错误状态(广播 idle 状态给所有客户端)
|
|
1086
1112
|
app.post('/api/rules/:id/clear-status', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1087
1113
|
const { id } = req.params;
|
|
@@ -1100,7 +1126,7 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
1100
1126
|
res.status(404).json({ error: 'Route not found' });
|
|
1101
1127
|
return;
|
|
1102
1128
|
}
|
|
1103
|
-
//
|
|
1129
|
+
// 标记规则为 idle 状态
|
|
1104
1130
|
rules_status_service_1.rulesStatusBroadcaster.markRuleIdle(route.id, id);
|
|
1105
1131
|
res.json({ success: true });
|
|
1106
1132
|
})));
|
|
@@ -1946,32 +1972,24 @@ const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
1946
1972
|
});
|
|
1947
1973
|
// 创建 WebSocket 服务器用于工具安装
|
|
1948
1974
|
const toolInstallWss = (0, websocket_service_1.createToolInstallationWSServer)();
|
|
1949
|
-
// 创建 WebSocket 服务器用于规则状态
|
|
1950
|
-
const rulesStatusWss = (0, rules_status_service_1.createRulesStatusWSServer)();
|
|
1951
1975
|
// 设置黑名单检查函数,用于在规则状态同步时检查黑名单是否已过期
|
|
1952
1976
|
rules_status_service_1.rulesStatusBroadcaster.setBlacklistChecker((serviceId, routeId, contentType) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1953
1977
|
// 检查服务��否在黑名单中
|
|
1954
1978
|
const isBlacklisted = yield dbManager.isServiceBlacklisted(serviceId, routeId, contentType);
|
|
1955
1979
|
return isBlacklisted;
|
|
1956
1980
|
}));
|
|
1957
|
-
// 将 WebSocket 服务器附加到 HTTP
|
|
1981
|
+
// 将 WebSocket 服务器附加到 HTTP 服务器(仅用于工具安装)
|
|
1958
1982
|
server.on('upgrade', (request, socket, head) => {
|
|
1959
1983
|
if (request.url === '/api/tools/install') {
|
|
1960
1984
|
toolInstallWss.handleUpgrade(request, socket, head, (ws) => {
|
|
1961
1985
|
toolInstallWss.emit('connection', ws, request);
|
|
1962
1986
|
});
|
|
1963
1987
|
}
|
|
1964
|
-
else if (request.url === '/api/rules/status') {
|
|
1965
|
-
rulesStatusWss.handleUpgrade(request, socket, head, (ws) => {
|
|
1966
|
-
rulesStatusWss.emit('connection', ws, request);
|
|
1967
|
-
});
|
|
1968
|
-
}
|
|
1969
1988
|
else {
|
|
1970
1989
|
socket.destroy();
|
|
1971
1990
|
}
|
|
1972
1991
|
});
|
|
1973
1992
|
console.log(`WebSocket server for tool installation attached to ws://${host}:${port}/api/tools/install`);
|
|
1974
|
-
console.log(`WebSocket server for rules status attached to ws://${host}:${port}/api/rules/status`);
|
|
1975
1993
|
let isShuttingDown = false;
|
|
1976
1994
|
let shutdownPromise = null;
|
|
1977
1995
|
const shutdown = (signal) => __awaiter(void 0, void 0, void 0, function* () {
|