const express = require('express') const router = express.Router() const logger = require('../utils/logger') const webhookService = require('../services/webhookService') const webhookConfigService = require('../services/webhookConfigService') const { authenticateAdmin } = require('../middleware/auth') const { getISOStringWithTimezone } = require('../utils/dateHelper') // 获取webhook配置 router.get('/config', authenticateAdmin, async (req, res) => { try { const config = await webhookConfigService.getConfig() res.json({ success: true, config }) } catch (error) { logger.error('获取webhook配置失败:', error) res.status(500).json({ error: 'Internal server error', message: '获取webhook配置失败' }) } }) // 保存webhook配置 router.post('/config', authenticateAdmin, async (req, res) => { try { const config = await webhookConfigService.saveConfig(req.body) res.json({ success: true, message: 'Webhook配置已保存', config }) } catch (error) { logger.error('保存webhook配置失败:', error) res.status(500).json({ error: 'Internal server error', message: error.message || '保存webhook配置失败' }) } }) // 添加webhook平台 router.post('/platforms', authenticateAdmin, async (req, res) => { try { const platform = await webhookConfigService.addPlatform(req.body) res.json({ success: true, message: 'Webhook平台已添加', platform }) } catch (error) { logger.error('添加webhook平台失败:', error) res.status(500).json({ error: 'Internal server error', message: error.message || '添加webhook平台失败' }) } }) // 更新webhook平台 router.put('/platforms/:id', authenticateAdmin, async (req, res) => { try { const platform = await webhookConfigService.updatePlatform(req.params.id, req.body) res.json({ success: true, message: 'Webhook平台已更新', platform }) } catch (error) { logger.error('更新webhook平台失败:', error) res.status(500).json({ error: 'Internal server error', message: error.message || '更新webhook平台失败' }) } }) // 删除webhook平台 router.delete('/platforms/:id', authenticateAdmin, async (req, res) => { try { await webhookConfigService.deletePlatform(req.params.id) res.json({ success: true, message: 'Webhook平台已删除' }) } catch (error) { logger.error('删除webhook平台失败:', error) res.status(500).json({ error: 'Internal server error', message: error.message || '删除webhook平台失败' }) } }) // 切换webhook平台启用状态 router.post('/platforms/:id/toggle', authenticateAdmin, async (req, res) => { try { const platform = await webhookConfigService.togglePlatform(req.params.id) res.json({ success: true, message: `Webhook平台已${platform.enabled ? '启用' : '禁用'}`, platform }) } catch (error) { logger.error('切换webhook平台状态失败:', error) res.status(500).json({ error: 'Internal server error', message: error.message || '切换webhook平台状态失败' }) } }) // 测试Webhook连通性 router.post('/test', authenticateAdmin, async (req, res) => { try { const { url, type = 'custom', secret, enableSign, deviceKey, serverUrl, level, sound, group, // SMTP 相关字段 host, port, secure, user, pass, from, to, ignoreTLS, botToken, chatId, apiBaseUrl, proxyUrl } = req.body // Bark平台特殊处理 if (type === 'bark') { if (!deviceKey) { return res.status(400).json({ error: 'Missing device key', message: '请提供Bark设备密钥' }) } // 验证服务器URL(如果提供) if (serverUrl) { try { new URL(serverUrl) } catch (urlError) { return res.status(400).json({ error: 'Invalid server URL format', message: '请提供有效的Bark服务器URL' }) } } logger.info(`🧪 测试webhook: ${type} - Device Key: ${deviceKey.substring(0, 8)}...`) } else if (type === 'smtp') { // SMTP平台验证 if (!host) { return res.status(400).json({ error: 'Missing SMTP host', message: '请提供SMTP服务器地址' }) } if (!user) { return res.status(400).json({ error: 'Missing SMTP user', message: '请提供SMTP用户名' }) } if (!pass) { return res.status(400).json({ error: 'Missing SMTP password', message: '请提供SMTP密码' }) } if (!to) { return res.status(400).json({ error: 'Missing recipient email', message: '请提供收件人邮箱' }) } logger.info(`🧪 测试webhook: ${type} - ${host}:${port || 587} -> ${to}`) } else if (type === 'telegram') { if (!botToken) { return res.status(400).json({ error: 'Missing Telegram bot token', message: '请提供 Telegram 机器人 Token' }) } if (!chatId) { return res.status(400).json({ error: 'Missing Telegram chat id', message: '请提供 Telegram Chat ID' }) } if (apiBaseUrl) { try { const parsed = new URL(apiBaseUrl) if (!['http:', 'https:'].includes(parsed.protocol)) { return res.status(400).json({ error: 'Invalid Telegram API base url protocol', message: 'Telegram API 基础地址仅支持 http 或 https' }) } } catch (urlError) { return res.status(400).json({ error: 'Invalid Telegram API base url', message: '请提供有效的 Telegram API 基础地址' }) } } if (proxyUrl) { try { const parsed = new URL(proxyUrl) const supportedProtocols = ['http:', 'https:', 'socks4:', 'socks4a:', 'socks5:'] if (!supportedProtocols.includes(parsed.protocol)) { return res.status(400).json({ error: 'Unsupported proxy protocol', message: 'Telegram 代理仅支持 http/https/socks 协议' }) } } catch (urlError) { return res.status(400).json({ error: 'Invalid proxy url', message: '请提供有效的代理地址' }) } } logger.info(`🧪 测试webhook: ${type} - Chat ID: ${chatId}`) } else { // 其他平台验证URL if (!url) { return res.status(400).json({ error: 'Missing webhook URL', message: '请提供webhook URL' }) } // 验证URL格式 try { new URL(url) } catch (urlError) { return res.status(400).json({ error: 'Invalid URL format', message: '请提供有效的webhook URL' }) } logger.info(`🧪 测试webhook: ${type} - ${url}`) } // 创建临时平台配置 const platform = { type, url, secret, enableSign, enabled: true, timeout: 10000 } // 添加Bark特有字段 if (type === 'bark') { platform.deviceKey = deviceKey platform.serverUrl = serverUrl platform.level = level platform.sound = sound platform.group = group } else if (type === 'smtp') { // 添加SMTP特有字段 platform.host = host platform.port = port || 587 platform.secure = secure || false platform.user = user platform.pass = pass platform.from = from platform.to = to platform.ignoreTLS = ignoreTLS || false } else if (type === 'telegram') { platform.botToken = botToken platform.chatId = chatId platform.apiBaseUrl = apiBaseUrl platform.proxyUrl = proxyUrl } const result = await webhookService.testWebhook(platform) const identifier = (() => { if (type === 'bark') { return `Device: ${deviceKey.substring(0, 8)}...` } if (type === 'smtp') { const recipients = Array.isArray(to) ? to.join(', ') : to return `${host}:${port || 587} -> ${recipients}` } if (type === 'telegram') { return `Chat ID: ${chatId}` } return url })() if (result.success) { logger.info(`✅ Webhook测试成功: ${identifier}`) res.json({ success: true, message: 'Webhook测试成功', url: type === 'bark' ? undefined : url, deviceKey: type === 'bark' ? `${deviceKey.substring(0, 8)}...` : undefined }) } else { logger.warn(`❌ Webhook测试失败: ${identifier} - ${result.error}`) res.status(400).json({ success: false, message: 'Webhook测试失败', url: type === 'bark' ? undefined : url, deviceKey: type === 'bark' ? `${deviceKey.substring(0, 8)}...` : undefined, error: result.error }) } } catch (error) { logger.error('❌ Webhook测试错误:', error) res.status(500).json({ error: 'Internal server error', message: '测试webhook失败' }) } }) // 手动触发测试通知 router.post('/test-notification', authenticateAdmin, async (req, res) => { try { const { type = 'test', accountId = 'test-account-id', accountName = '测试账号', platform = 'claude-oauth', status = 'test', errorCode = 'TEST_NOTIFICATION', reason = '手动测试通知', message = '这是一条测试通知消息,用于验证 Webhook 通知功能是否正常工作' } = req.body logger.info(`🧪 发送测试通知: ${type}`) // 先检查webhook配置 const config = await webhookConfigService.getConfig() logger.debug( `Webhook配置: enabled=${config.enabled}, platforms=${config.platforms?.length || 0}` ) if (!config.enabled) { return res.status(400).json({ success: false, message: 'Webhook通知未启用,请先在设置中启用通知功能' }) } const enabledPlatforms = await webhookConfigService.getEnabledPlatforms() logger.info(`找到 ${enabledPlatforms.length} 个启用的通知平台`) if (enabledPlatforms.length === 0) { return res.status(400).json({ success: false, message: '没有启用的通知平台,请先添加并启用至少一个通知平台' }) } const testData = { accountId, accountName, platform, status, errorCode, reason, message, timestamp: getISOStringWithTimezone(new Date()) } const result = await webhookService.sendNotification(type, testData) // 如果没有返回结果,说明可能是配置问题 if (!result) { return res.status(400).json({ success: false, message: 'Webhook服务未返回结果,请检查配置和日志', enabledPlatforms: enabledPlatforms.length }) } // 如果没有成功和失败的记录 if (result.succeeded === 0 && result.failed === 0) { return res.status(400).json({ success: false, message: '没有发送任何通知,请检查通知类型配置', result, enabledPlatforms: enabledPlatforms.length }) } if (result.failed > 0) { logger.warn(`⚠️ 测试通知部分失败: ${result.succeeded}成功, ${result.failed}失败`) return res.json({ success: true, message: `测试通知部分成功: ${result.succeeded}个平台成功, ${result.failed}个平台失败`, data: testData, result }) } logger.info(`✅ 测试通知发送成功到 ${result.succeeded} 个平台`) res.json({ success: true, message: `测试通知已成功发送到 ${result.succeeded} 个平台`, data: testData, result }) } catch (error) { logger.error('❌ 发送测试通知失败:', error) res.status(500).json({ error: 'Internal server error', message: `发送测试通知失败: ${error.message}` }) } }) module.exports = router