mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 09:38:02 +00:00
feat: 添加账号异常状态 Webhook 通知功能
## 功能概述
- 新增账号禁用/异常状态的 Webhook 实时通知机制
- 支持 Claude OAuth、Claude Console、Gemini 三种平台的账号监控
- 提供完整的 Webhook 管理 API 和配置选项
## 主要变更
### 新增文件
- `src/utils/webhookNotifier.js`: Webhook 通知核心服务
- `src/routes/webhook.js`: Webhook 管理 API 路由
### 功能集成
- Claude OAuth 账号:unauthorized 状态 + token 刷新错误通知
- Claude Console 账号:blocked 状态通知
- Gemini 账号:token 刷新错误通知
### 配置支持
- 新增环境变量:WEBHOOK_ENABLED, WEBHOOK_URLS, WEBHOOK_TIMEOUT, WEBHOOK_RETRIES
- 支持多个 Webhook URL 并发通知
- 自动重试机制(指数退避)+ 超时保护
### 管理端点
- POST /admin/webhook/test: 测试连通性
- POST /admin/webhook/test-notification: 发送测试通知
- GET /admin/webhook/config: 查看配置信息
## 通知格式
```json
{
"type": "account_anomaly",
"data": {
"accountId": "uuid",
"accountName": "账号名称",
"platform": "claude-oauth|claude-console|gemini",
"status": "unauthorized|blocked|error",
"errorCode": "CLAUDE_OAUTH_UNAUTHORIZED",
"reason": "具体异常原因",
"timestamp": "2025-01-13T10:30:00.000Z"
}
}
```
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
120
src/routes/webhook.js
Normal file
120
src/routes/webhook.js
Normal file
@@ -0,0 +1,120 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const logger = require('../utils/logger')
|
||||
const webhookNotifier = require('../utils/webhookNotifier')
|
||||
const { authenticateAdmin } = require('../middleware/auth')
|
||||
|
||||
// 测试Webhook连通性
|
||||
router.post('/test', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const { url } = req.body
|
||||
|
||||
if (!url) {
|
||||
return res.status(400).json({
|
||||
error: 'Missing webhook URL',
|
||||
message: 'Please provide a webhook URL to test'
|
||||
})
|
||||
}
|
||||
|
||||
// 验证URL格式
|
||||
try {
|
||||
new URL(url)
|
||||
} catch (urlError) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid URL format',
|
||||
message: 'Please provide a valid webhook URL'
|
||||
})
|
||||
}
|
||||
|
||||
logger.info(`🧪 Testing webhook URL: ${url}`)
|
||||
|
||||
const result = await webhookNotifier.testWebhook(url)
|
||||
|
||||
if (result.success) {
|
||||
logger.info(`✅ Webhook test successful for: ${url}`)
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Webhook test successful',
|
||||
url: url
|
||||
})
|
||||
} else {
|
||||
logger.warn(`❌ Webhook test failed for: ${url} - ${result.error}`)
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: 'Webhook test failed',
|
||||
url: url,
|
||||
error: result.error
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('❌ Webhook test error:', error)
|
||||
res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to test webhook'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 手动触发账号异常通知(用于测试)
|
||||
router.post('/test-notification', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
accountId = 'test-account-id',
|
||||
accountName = 'Test Account',
|
||||
platform = 'claude-oauth',
|
||||
status = 'error',
|
||||
errorCode = 'TEST_ERROR',
|
||||
reason = 'Manual test notification'
|
||||
} = req.body
|
||||
|
||||
logger.info(`🧪 Sending test notification for account: ${accountName}`)
|
||||
|
||||
await webhookNotifier.sendAccountAnomalyNotification({
|
||||
accountId,
|
||||
accountName,
|
||||
platform,
|
||||
status,
|
||||
errorCode,
|
||||
reason
|
||||
})
|
||||
|
||||
logger.info(`✅ Test notification sent successfully`)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Test notification sent successfully',
|
||||
data: {
|
||||
accountId,
|
||||
accountName,
|
||||
platform,
|
||||
status,
|
||||
errorCode,
|
||||
reason
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to send test notification:', error)
|
||||
res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
message: 'Failed to send test notification'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 获取Webhook配置信息
|
||||
router.get('/config', authenticateAdmin, (req, res) => {
|
||||
const config = require('../../config/config')
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
config: {
|
||||
enabled: config.webhook?.enabled !== false,
|
||||
urls: config.webhook?.urls || [],
|
||||
timeout: config.webhook?.timeout || 10000,
|
||||
retries: config.webhook?.retries || 3,
|
||||
urlCount: (config.webhook?.urls || []).length
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = router
|
||||
Reference in New Issue
Block a user