const express = require('express'); const router = express.Router(); const ClashParser = require('../core/clashParser'); const SpeedTester = require('../core/speedTester'); const TelegramNotifier = require('../core/notifier'); const SubscriptionManager = require('../core/subscriptionManager'); const { Node, TestResult, Notification } = require('../models'); const logger = require('../utils/logger'); // 节点管理API router.get('/nodes', async (req, res) => { try { const { page = 1, limit = 20, group, status, search } = req.query; const offset = (page - 1) * limit; const where = {}; if (group) where.group = group; if (status) where.status = status; if (search) { where[require('sequelize').Op.or] = [ { name: { [require('sequelize').Op.like]: `%${search}%` } }, { server: { [require('sequelize').Op.like]: `%${search}%` } } ]; } const { count, rows } = await Node.findAndCountAll({ where, limit: parseInt(limit), offset: parseInt(offset), order: [['createdAt', 'DESC']], include: [{ model: TestResult, as: 'testResults', limit: 1, order: [['testTime', 'DESC']] }] }); res.json({ success: true, data: { nodes: rows, pagination: { page: parseInt(page), limit: parseInt(limit), total: count, pages: Math.ceil(count / limit) } } }); } catch (error) { logger.error('获取节点列表失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); router.get('/nodes/:id', async (req, res) => { try { const node = await Node.findByPk(req.params.id, { include: [{ model: TestResult, as: 'testResults', limit: 50, order: [['testTime', 'DESC']] }] }); if (!node) { return res.status(404).json({ success: false, error: '节点不存在' }); } res.json({ success: true, data: node }); } catch (error) { logger.error('获取节点详情失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); router.post('/nodes', async (req, res) => { try { const nodeData = req.body; const node = await Node.create(nodeData); logger.info('创建节点成功', { nodeId: node.id, nodeName: node.name }); res.json({ success: true, data: node }); } catch (error) { logger.error('创建节点失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); router.put('/nodes/:id', async (req, res) => { try { const node = await Node.findByPk(req.params.id); if (!node) { return res.status(404).json({ success: false, error: '节点不存在' }); } await node.update(req.body); logger.info('更新节点成功', { nodeId: node.id, nodeName: node.name }); res.json({ success: true, data: node }); } catch (error) { logger.error('更新节点失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); router.delete('/nodes/:id', async (req, res) => { try { const node = await Node.findByPk(req.params.id); if (!node) { return res.status(404).json({ success: false, error: '节点不存在' }); } await node.destroy(); logger.info('删除节点成功', { nodeId: node.id, nodeName: node.name }); res.json({ success: true }); } catch (error) { logger.error('删除节点失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); // 导入Clash配置 router.post('/import/clash', async (req, res) => { try { const { configPath, configUrl } = req.body; if (!configPath && !configUrl) { return res.status(400).json({ success: false, error: '请提供配置文件路径或URL' }); } const parser = new ClashParser(configPath || './temp_config.yaml'); let nodes; if (configUrl) { nodes = await parser.importFromUrl(configUrl); } else { nodes = await parser.parseConfig(); } // 批量创建节点 const createdNodes = []; for (const nodeData of nodes) { try { const existingNode = await Node.findOne({ where: { name: nodeData.name, server: nodeData.server, port: nodeData.port } }); if (!existingNode) { const node = await Node.create(nodeData); createdNodes.push(node); } } catch (error) { logger.warn('创建节点失败', { nodeData, error: error.message }); } } logger.info('导入Clash配置成功', { imported: createdNodes.length }); res.json({ success: true, data: { imported: createdNodes.length, nodes: createdNodes } }); } catch (error) { logger.error('导入Clash配置失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); // 订阅管理API router.post('/subscription/update', async (req, res) => { try { const subscriptionManager = new SubscriptionManager(); const result = await subscriptionManager.manualUpdate(); if (result.success) { res.json({ success: true, data: result.data }); } else { res.status(400).json({ success: false, error: result.error }); } } catch (error) { logger.error('手动更新订阅失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); router.get('/subscription/status', async (req, res) => { try { const subscriptionManager = new SubscriptionManager(); const status = subscriptionManager.getStatus(); res.json({ success: true, data: status }); } catch (error) { logger.error('获取订阅状态失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); // 测试相关API router.post('/test/manual', async (req, res) => { try { const { nodeIds } = req.body; let nodes; if (nodeIds && nodeIds.length > 0) { nodes = await Node.findAll({ where: { id: nodeIds } }); } else { nodes = await Node.findAll({ where: { isActive: true } }); } if (nodes.length === 0) { return res.status(400).json({ success: false, error: '没有找到要测试的节点' }); } const speedTester = new SpeedTester(); const results = await speedTester.testNodes(nodes); // 处理测试结果并更新节点状态 const scheduler = new (require('../core/scheduler'))(); await scheduler.processTestResults(nodes, results); // 发送测试总结报告 await scheduler.sendTestSummary(nodes, results); logger.info('手动测试完成', { testedNodes: nodes.length }); res.json({ success: true, data: { results, testedNodes: nodes.length } }); } catch (error) { logger.error('手动测试失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); // Ping测试API - 参考Electron应用实现 router.post('/test/ping', async (req, res) => { try { const { host, port, attempts = 3, timeout = 2000 } = req.body; if (!host || !port) { return res.status(400).json({ success: false, error: '请提供host和port参数' }); } const speedTester = new SpeedTester(); const result = await speedTester.pingHosts(host, port, attempts, timeout); logger.info('Ping测试完成', { host, port, success: result.success }); res.json({ success: true, data: result }); } catch (error) { logger.error('Ping测试失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); // 批量Ping测试API - 参考Electron应用实现 router.post('/test/ping/batch', async (req, res) => { try { const { hosts, attempts = 3, timeout = 2000 } = req.body; if (!hosts || !Array.isArray(hosts) || hosts.length === 0) { return res.status(400).json({ success: false, error: '请提供hosts数组参数' }); } const speedTester = new SpeedTester(); const result = await speedTester.batchPingHosts(hosts); logger.info('批量Ping测试完成', { testedHosts: hosts.length }); res.json({ success: true, data: result }); } catch (error) { logger.error('批量Ping测试失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); router.get('/test/results', async (req, res) => { try { const { page = 1, limit = 50, nodeId, nodeName, isSuccess, startDate, endDate } = req.query; const offset = (page - 1) * limit; const where = {}; if (nodeId) where.nodeId = nodeId; if (isSuccess !== undefined) where.isSuccess = isSuccess === 'true'; if (startDate || endDate) { where.testTime = {}; if (startDate) where.testTime[require('sequelize').Op.gte] = new Date(startDate); if (endDate) where.testTime[require('sequelize').Op.lte] = new Date(endDate); } const includeOptions = [{ model: Node, as: 'node', attributes: ['name', 'type', 'group', 'averageLatency'] }]; // 如果提供了节点名称,添加节点名称过滤条件 if (nodeName) { includeOptions[0].where = { name: { [require('sequelize').Op.like]: `%${nodeName}%` } }; } const { count, rows } = await TestResult.findAndCountAll({ where, limit: parseInt(limit), offset: parseInt(offset), order: [['testTime', 'DESC']], include: includeOptions }); res.json({ success: true, data: { results: rows, pagination: { page: parseInt(page), limit: parseInt(limit), total: count, pages: Math.ceil(count / limit) } } }); } catch (error) { logger.error('获取测试结果失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); // 通知相关API router.get('/notifications', async (req, res) => { try { const { page = 1, limit = 20, type, isSent } = req.query; const offset = (page - 1) * limit; const where = {}; if (type) where.type = type; if (isSent !== undefined) where.isSent = isSent === 'true'; const { count, rows } = await Notification.findAndCountAll({ where, limit: parseInt(limit), offset: parseInt(offset), order: [['createdAt', 'DESC']], include: [{ model: Node, as: 'node', attributes: ['name', 'group'] }] }); res.json({ success: true, data: { notifications: rows, pagination: { page: parseInt(page), limit: parseInt(limit), total: count, pages: Math.ceil(count / limit) } } }); } catch (error) { logger.error('获取通知列表失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); router.post('/notifications/test', async (req, res) => { try { const notifier = new TelegramNotifier(); const result = await notifier.testConnection(); if (result.success) { res.json({ success: true, data: result }); } else { res.status(400).json({ success: false, error: result.error }); } } catch (error) { logger.error('测试通知连接失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); // 统计信息API router.get('/stats', async (req, res) => { try { const [ totalNodes, onlineNodes, offlineNodes, totalTests, successfulTests, totalNotifications ] = await Promise.all([ Node.count(), Node.count({ where: { status: 'online' } }), Node.count({ where: { status: 'offline' } }), TestResult.count(), TestResult.count({ where: { isSuccess: true } }), Notification.count() ]); // 获取最近24小时的测试统计 const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); const recentTests = await TestResult.count({ where: { testTime: { [require('sequelize').Op.gte]: yesterday } } }); const recentSuccessfulTests = await TestResult.count({ where: { testTime: { [require('sequelize').Op.gte]: yesterday }, isSuccess: true } }); const stats = { nodes: { total: totalNodes, online: onlineNodes, offline: offlineNodes, onlineRate: totalNodes > 0 ? Math.round((onlineNodes / totalNodes) * 100) : 0 }, tests: { total: totalTests, successful: successfulTests, successRate: totalTests > 0 ? Math.round((successfulTests / totalTests) * 100) : 0, recent24h: recentTests, recent24hSuccess: recentSuccessfulTests, recent24hSuccessRate: recentTests > 0 ? Math.round((recentSuccessfulTests / recentTests) * 100) : 0 }, notifications: { total: totalNotifications } }; res.json({ success: true, data: stats }); } catch (error) { logger.error('获取统计信息失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); // 系统状态API router.get('/status', async (req, res) => { try { const scheduler = req.app.get('scheduler'); const status = scheduler ? scheduler.getStatus() : { error: '调度器未初始化' }; res.json({ success: true, data: status }); } catch (error) { logger.error('获取系统状态失败', { error: error.message }); res.status(500).json({ success: false, error: error.message }); } }); module.exports = router;