const axios = require('axios');
const logger = require('../utils/logger');
const { getDatabase } = require('../config/database');
class WebhookService {
constructor() {
this.timeout = 30000;
this.retryCount = 3;
this.retryDelay = 1000;
}
async push(webhookUrl, data, retryCount = 0) {
try {
logger.info(`推送数据到Webhook: ${webhookUrl}`);
const payload = {
timestamp: new Date().toISOString(),
type: 'access_token_update',
data: data
};
const response = await axios.post(webhookUrl, payload, {
timeout: this.timeout,
headers: {
'Content-Type': 'application/json',
'User-Agent': 'WeChatTokenManager/1.0'
}
});
await this.logWebhookCall(webhookUrl, payload, response.status, response.data, true);
logger.info(`Webhook推送成功 - 状态码: ${response.status}`);
return {
success: true,
statusCode: response.status,
response: response.data
};
} catch (error) {
logger.error(`Webhook推送失败 (尝试 ${retryCount + 1}/${this.retryCount + 1}):`, error.message);
await this.logWebhookCall(
webhookUrl,
{ timestamp: new Date().toISOString(), type: 'access_token_update', data: data },
error.response?.status || 0,
error.message,
false
);
if (retryCount < this.retryCount) {
logger.info(`${this.retryDelay / 1000}秒后进行第${retryCount + 2}次重试...`);
await this.sleep(this.retryDelay);
return this.push(webhookUrl, data, retryCount + 1);
}
throw error;
}
}
async testWebhook(webhookUrl) {
try {
const testPayload = {
timestamp: new Date().toISOString(),
type: 'test',
message: '这是一个测试请求'
};
const response = await axios.post(webhookUrl, testPayload, {
timeout: 10000,
headers: {
'Content-Type': 'application/json',
'User-Agent': 'WeChatTokenManager/1.0'
}
});
return {
success: true,
statusCode: response.status,
message: 'Webhook连接正常'
};
} catch (error) {
return {
success: false,
statusCode: error.response?.status || 0,
message: error.message
};
}
}
async logWebhookCall(url, payload, statusCode, responseBody, success) {
const db = getDatabase();
return new Promise((resolve, reject) => {
const sql = `
INSERT INTO webhook_logs (webhook_url, payload, status_code, response_body, success)
VALUES (?, ?, ?, ?, ?)
`;
db.run(sql, [
url,
JSON.stringify(payload),
statusCode,
typeof responseBody === 'object' ? JSON.stringify(responseBody) : responseBody,
success ? 1 : 0
], function(err) {
if (err) {
logger.error('记录Webhook日志失败:', err);
reject(err);
} else {
resolve(this.lastID);
}
});
});
}
async getWebhookLogs(limit = 50) {
const db = getDatabase();
return new Promise((resolve, reject) => {
const sql = `
SELECT * FROM webhook_logs
ORDER BY created_at DESC
LIMIT ?
`;
db.all(sql, [limit], (err, rows) => {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
module.exports = new WebhookService();