aicodeswitch 1.10.2 → 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.
@@ -17,11 +17,13 @@ const cors_1 = __importDefault(require("cors"));
17
17
  const dotenv_1 = __importDefault(require("dotenv"));
18
18
  const fs_1 = __importDefault(require("fs"));
19
19
  const path_1 = __importDefault(require("path"));
20
+ const crypto_1 = require("crypto");
20
21
  const database_1 = require("./database");
21
22
  const proxy_server_1 = require("./proxy-server");
22
23
  const os_1 = __importDefault(require("os"));
23
24
  const auth_1 = require("./auth");
24
25
  const version_check_1 = require("./version-check");
26
+ const utils_1 = require("./utils");
25
27
  const dotenvPath = path_1.default.resolve(os_1.default.homedir(), '.aicodeswitch/aicodeswitch.conf');
26
28
  if (fs_1.default.existsSync(dotenvPath)) {
27
29
  dotenv_1.default.config({ path: dotenvPath });
@@ -343,18 +345,6 @@ const registerRoutes = (dbManager, proxyServer) => {
343
345
  yield dbManager.clearLogs();
344
346
  res.json(true);
345
347
  })));
346
- app.get('/api/access-logs', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
347
- const rawLimit = typeof req.query.limit === 'string' ? parseInt(req.query.limit, 10) : NaN;
348
- const rawOffset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : NaN;
349
- const limit = Number.isFinite(rawLimit) ? rawLimit : 100;
350
- const offset = Number.isFinite(rawOffset) ? rawOffset : 0;
351
- const logs = yield dbManager.getAccessLogs(limit, offset);
352
- res.json(logs);
353
- })));
354
- app.delete('/api/access-logs', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
355
- yield dbManager.clearAccessLogs();
356
- res.json(true);
357
- })));
358
348
  app.get('/api/error-logs', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
359
349
  const rawLimit = typeof req.query.limit === 'string' ? parseInt(req.query.limit, 10) : NaN;
360
350
  const rawOffset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : NaN;
@@ -371,10 +361,6 @@ const registerRoutes = (dbManager, proxyServer) => {
371
361
  const count = yield dbManager.getLogsCount();
372
362
  res.json({ count });
373
363
  })));
374
- app.get('/api/access-logs/count', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
375
- const count = yield dbManager.getAccessLogsCount();
376
- res.json({ count });
377
- })));
378
364
  app.get('/api/error-logs/count', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
379
365
  const count = yield dbManager.getErrorLogsCount();
380
366
  res.json({ count });
@@ -432,6 +418,41 @@ const registerRoutes = (dbManager, proxyServer) => {
432
418
  const stats = yield dbManager.getStatistics(days);
433
419
  res.json(stats);
434
420
  })));
421
+ // Sessions 相关端点
422
+ app.get('/api/sessions', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
423
+ const rawLimit = typeof req.query.limit === 'string' ? parseInt(req.query.limit, 10) : NaN;
424
+ const rawOffset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : NaN;
425
+ const limit = Number.isFinite(rawLimit) ? rawLimit : 100;
426
+ const offset = Number.isFinite(rawOffset) ? rawOffset : 0;
427
+ const sessions = dbManager.getSessions(limit, offset);
428
+ res.json(sessions);
429
+ })));
430
+ app.get('/api/sessions/count', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
431
+ const count = dbManager.getSessionsCount();
432
+ res.json({ count });
433
+ })));
434
+ app.get('/api/sessions/:id', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
435
+ const session = dbManager.getSession(req.params.id);
436
+ if (!session) {
437
+ res.status(404).json({ error: 'Session not found' });
438
+ return;
439
+ }
440
+ res.json(session);
441
+ })));
442
+ app.get('/api/sessions/:id/logs', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
443
+ const rawLimit = typeof req.query.limit === 'string' ? parseInt(req.query.limit, 10) : NaN;
444
+ const limit = Number.isFinite(rawLimit) ? rawLimit : 100;
445
+ const logs = yield dbManager.getLogsBySessionId(req.params.id, limit);
446
+ res.json(logs);
447
+ })));
448
+ app.delete('/api/sessions/:id', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
449
+ const result = dbManager.deleteSession(req.params.id);
450
+ res.json(result);
451
+ })));
452
+ app.delete('/api/sessions', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
453
+ dbManager.clearSessions();
454
+ res.json(true);
455
+ })));
435
456
  app.get('/api/docs/recommend-vendors', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
436
457
  const resp = yield fetch('https://unpkg.com/aicodeswitch/docs/vendors-recommand.md');
437
458
  if (!resp.ok) {
@@ -450,24 +471,97 @@ const registerRoutes = (dbManager, proxyServer) => {
450
471
  const text = yield resp.text();
451
472
  res.type('text/plain').send(text);
452
473
  })));
453
- app.use(express_1.default.static(path_1.default.resolve(__dirname, '../ui')));
474
+ // Migration 相关端点
475
+ const getMigrationHashPath = () => path_1.default.join(dataDir, '.migration-hash');
476
+ // 查找 migration.md 文件的路径
477
+ const findMigrationPath = () => {
478
+ // 可能的路径列表
479
+ const possiblePaths = [
480
+ // 开发环境:src/server/main.ts -> public/migration.md
481
+ path_1.default.resolve(__dirname, '../../public/migration.md'),
482
+ // 生产环境:dist/server/main.js -> dist/ui/migration.md
483
+ path_1.default.resolve(__dirname, '../ui/migration.md'),
484
+ ];
485
+ for (const possiblePath of possiblePaths) {
486
+ if (fs_1.default.existsSync(possiblePath)) {
487
+ return possiblePath;
488
+ }
489
+ }
490
+ return null;
491
+ };
492
+ app.get('/api/migration', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
493
+ try {
494
+ // 读取 migration.md 文件
495
+ const migrationPath = findMigrationPath();
496
+ if (!migrationPath) {
497
+ res.json({ shouldShow: false, content: '' });
498
+ return;
499
+ }
500
+ const content = fs_1.default.readFileSync(migrationPath, 'utf-8');
501
+ // 计算当前内容的 hash
502
+ const currentHash = (0, crypto_1.createHash)('sha256').update(content).digest('hex');
503
+ // 读取之前保存的 hash
504
+ const hashPath = getMigrationHashPath();
505
+ let savedHash = '';
506
+ if (fs_1.default.existsSync(hashPath)) {
507
+ savedHash = fs_1.default.readFileSync(hashPath, 'utf-8').trim();
508
+ }
509
+ // 如果 hash 不同,需要显示弹窗
510
+ const shouldShow = savedHash !== currentHash;
511
+ res.json({ shouldShow, content: shouldShow ? content : '' });
512
+ }
513
+ catch (error) {
514
+ console.error('Failed to read migration file:', error);
515
+ res.json({ shouldShow: false, content: '' });
516
+ }
517
+ })));
518
+ app.post('/api/migration/ack', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
519
+ try {
520
+ // 读取 migration.md 文件并计算 hash
521
+ const migrationPath = findMigrationPath();
522
+ if (!migrationPath) {
523
+ res.json({ success: false });
524
+ return;
525
+ }
526
+ const content = fs_1.default.readFileSync(migrationPath, 'utf-8');
527
+ const hash = (0, crypto_1.createHash)('sha256').update(content).digest('hex');
528
+ // 保存 hash 到文件
529
+ const hashPath = getMigrationHashPath();
530
+ fs_1.default.writeFileSync(hashPath, hash, 'utf-8');
531
+ res.json({ success: true });
532
+ }
533
+ catch (error) {
534
+ console.error('Failed to acknowledge migration:', error);
535
+ res.json({ success: false });
536
+ }
537
+ })));
454
538
  };
455
539
  const start = () => __awaiter(void 0, void 0, void 0, function* () {
456
540
  fs_1.default.mkdirSync(dataDir, { recursive: true });
457
541
  const dbManager = new database_1.DatabaseManager(dataDir);
458
- yield dbManager.initialize();
459
542
  const proxyServer = new proxy_server_1.ProxyServer(dbManager, app);
543
+ yield dbManager.initialize();
544
+ // Initialize proxy server and register proxy routes last
545
+ proxyServer.initialize();
460
546
  // Register admin routes first
461
547
  registerRoutes(dbManager, proxyServer);
462
- // Initialize proxy server and register proxy routes last
463
- yield proxyServer.initialize();
464
- const adminServer = app.listen(port, host, () => {
548
+ yield proxyServer.registerProxyRoutes();
549
+ app.use(express_1.default.static(path_1.default.resolve(__dirname, '../ui')));
550
+ const isPortUsable = yield (0, utils_1.checkPortUsable)(port);
551
+ if (!isPortUsable) {
552
+ console.error(`端口 ${port} 已被占用,无法启动服务。请执行 aicos stop 后重启。`);
553
+ process.exit(1);
554
+ }
555
+ const server = app.listen(port, host, () => {
465
556
  console.log(`Admin server running on http://${host}:${port}`);
466
557
  });
467
558
  const shutdown = () => __awaiter(void 0, void 0, void 0, function* () {
468
559
  console.log('Shutting down server...');
469
560
  dbManager.close();
470
- adminServer.close(() => process.exit(0));
561
+ server.close(() => {
562
+ console.log('Server stopped.');
563
+ process.exit(0);
564
+ });
471
565
  });
472
566
  process.on('SIGINT', shutdown);
473
567
  process.on('SIGTERM', shutdown);