|
@@ -10,6 +10,7 @@ class MultiSubscriptionManager {
|
|
|
this.updateInterval = parseInt(process.env.SUBSCRIPTION_UPDATE_INTERVAL) || 3600000; // 默认1小时
|
|
|
this.updateTimer = null;
|
|
|
this.isUpdating = false; // 添加更新锁,防止并发更新
|
|
|
+ this.speedTestDebounceTimer = null; // 防抖定时器
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -55,27 +56,22 @@ class MultiSubscriptionManager {
|
|
|
try {
|
|
|
config = yaml.parse(rawContent);
|
|
|
if (config && typeof config === 'object' && Array.isArray(config.proxies)) {
|
|
|
- logger.info('成功解析为YAML格式', { subscriptionId: subscription.id });
|
|
|
yamlParsed = true;
|
|
|
} else {
|
|
|
- logger.info('YAML解析结果不是有效的订阅对象,进入Base64解码流程', { subscriptionId: subscription.id });
|
|
|
throw new Error('不是有效的YAML订阅');
|
|
|
}
|
|
|
} catch (error) {
|
|
|
- if (!yamlParsed) logger.info('不是YAML格式,尝试Base64解码', { subscriptionId: subscription.id });
|
|
|
+ // 尝试Base64解码
|
|
|
|
|
|
// 2. 不是YAML,尝试整体Base64解码
|
|
|
let decoded;
|
|
|
try {
|
|
|
decoded = Buffer.from(rawContent, 'base64').toString('utf8');
|
|
|
- logger.info('Base64解码成功,内容长度:', decoded.length, { subscriptionId: subscription.id });
|
|
|
|
|
|
// 如果解码后包含ss://、vmess://、trojan://,说明是明文链接合集
|
|
|
if (/ss:\/\/|vmess:\/\/|trojan:\/\//.test(decoded)) {
|
|
|
- logger.info('检测到代理链接,转换为Clash格式', { subscriptionId: subscription.id });
|
|
|
config = this.convertShadowsocksToClash(decoded);
|
|
|
} else {
|
|
|
- logger.info('整体解码后不是代理链接,尝试多行Base64解码', { subscriptionId: subscription.id });
|
|
|
// 3. 如果整体解码后不是明文链接合集,尝试多行Base64(每行一个链接)
|
|
|
const lines = rawContent.split('\n').filter(line => line.trim());
|
|
|
logger.info('原始内容行数:', lines.length, { subscriptionId: subscription.id });
|
|
@@ -117,16 +113,7 @@ class MultiSubscriptionManager {
|
|
|
throw new Error('订阅配置中没有找到有效的代理节点');
|
|
|
}
|
|
|
|
|
|
- // 只在节点数量较多时显示前几个名称
|
|
|
- const proxyNames = config.proxies.length > 10
|
|
|
- ? config.proxies.slice(0, 3).map(p => p.name)
|
|
|
- : config.proxies.map(p => p.name);
|
|
|
-
|
|
|
- logger.info('订阅配置解析成功', {
|
|
|
- proxyCount: config.proxies.length,
|
|
|
- proxyNames: proxyNames,
|
|
|
- subscriptionId: subscription.id
|
|
|
- });
|
|
|
+ // 解析成功
|
|
|
|
|
|
return config;
|
|
|
} catch (error) {
|
|
@@ -178,10 +165,7 @@ class MultiSubscriptionManager {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- logger.info(`解析完成,去重后节点数量: ${newNodes.length}`, {
|
|
|
- subscriptionId: subscription.id,
|
|
|
- originalCount: config.proxies.length
|
|
|
- });
|
|
|
+ // 解析完成,去重后节点数量: ${newNodes.length}
|
|
|
|
|
|
if (newNodes.length === 0) {
|
|
|
logger.warn('订阅中没有找到有效的节点', { subscriptionId: subscription.id });
|
|
@@ -280,17 +264,17 @@ class MultiSubscriptionManager {
|
|
|
// 提交事务
|
|
|
await transaction.commit();
|
|
|
|
|
|
- logger.info(`订阅节点更新完成 - 新增${added}个,更新${updated}个,移除${removed}个,实际节点数${actualNodeCount}`, {
|
|
|
- subscriptionId: subscription.id,
|
|
|
- subscriptionName: subscription.name
|
|
|
- });
|
|
|
+ logger.info(`订阅更新: ${subscription.name} - 新增${added}个,更新${updated}个,移除${removed}个`);
|
|
|
|
|
|
// 如果有新增或更新的节点,触发测速
|
|
|
if (added > 0 || updated > 0) {
|
|
|
- logger.info('检测到节点更新,准备触发测速...');
|
|
|
// 延迟5秒后触发测速,确保数据库操作完成
|
|
|
- setTimeout(() => {
|
|
|
+ if (this.speedTestDebounceTimer) {
|
|
|
+ clearTimeout(this.speedTestDebounceTimer);
|
|
|
+ }
|
|
|
+ this.speedTestDebounceTimer = setTimeout(() => {
|
|
|
this.triggerSpeedTest();
|
|
|
+ this.speedTestDebounceTimer = null;
|
|
|
}, 5000);
|
|
|
}
|
|
|
|
|
@@ -320,17 +304,13 @@ class MultiSubscriptionManager {
|
|
|
|
|
|
try {
|
|
|
const subscriptions = await this.getActiveSubscriptions();
|
|
|
- logger.info(`开始更新所有订阅,共${subscriptions.length}个活跃订阅`);
|
|
|
+ logger.info(`更新 ${subscriptions.length} 个订阅`);
|
|
|
|
|
|
const results = [];
|
|
|
|
|
|
// 串行执行订阅更新,避免并发问题
|
|
|
for (const subscription of subscriptions) {
|
|
|
try {
|
|
|
- logger.info(`开始更新订阅: ${subscription.name}`, {
|
|
|
- subscriptionId: subscription.id
|
|
|
- });
|
|
|
-
|
|
|
const result = await this.updateSubscriptionNodes(subscription);
|
|
|
results.push({
|
|
|
subscriptionId: subscription.id,
|
|
@@ -338,14 +318,6 @@ class MultiSubscriptionManager {
|
|
|
...result
|
|
|
});
|
|
|
|
|
|
- logger.info(`订阅更新完成: ${subscription.name}`, {
|
|
|
- subscriptionId: subscription.id,
|
|
|
- added: result.added,
|
|
|
- updated: result.updated,
|
|
|
- removed: result.removed,
|
|
|
- actualNodeCount: result.actualNodeCount
|
|
|
- });
|
|
|
-
|
|
|
// 每个订阅更新后稍作延迟,避免过于频繁的数据库操作
|
|
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
|
|
|
@@ -369,10 +341,13 @@ class MultiSubscriptionManager {
|
|
|
);
|
|
|
|
|
|
if (hasUpdates) {
|
|
|
- logger.info('检测到节点更新,准备触发测速...');
|
|
|
// 延迟5秒后触发测速,确保数据库操作完成
|
|
|
- setTimeout(() => {
|
|
|
+ if (this.speedTestDebounceTimer) {
|
|
|
+ clearTimeout(this.speedTestDebounceTimer);
|
|
|
+ }
|
|
|
+ this.speedTestDebounceTimer = setTimeout(() => {
|
|
|
this.triggerSpeedTest();
|
|
|
+ this.speedTestDebounceTimer = null;
|
|
|
}, 5000);
|
|
|
}
|
|
|
|
|
@@ -471,7 +446,7 @@ class MultiSubscriptionManager {
|
|
|
const proxies = [];
|
|
|
const proxyKeys = new Set(); // 用于去重
|
|
|
|
|
|
- logger.info(`开始解析Shadowsocks内容,共${lines.length}行`);
|
|
|
+ // 开始解析Shadowsocks内容
|
|
|
|
|
|
let ssCount = 0;
|
|
|
let vmessCount = 0;
|
|
@@ -509,7 +484,7 @@ class MultiSubscriptionManager {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- logger.info(`解析完成 - SS: ${ssCount}个, VMess: ${vmessCount}个, Trojan: ${trojanCount}个, 成功解析: ${proxies.length}个, 重复跳过: ${duplicateCount}个, 失败: ${errorCount}个`);
|
|
|
+ // 解析完成 - SS: ${ssCount}个, VMess: ${vmessCount}个, Trojan: ${trojanCount}个
|
|
|
|
|
|
if (proxies.length === 0) {
|
|
|
logger.warn('没有找到任何有效的代理节点');
|