Jelajahi Sumber

server正则验证

Taio_O 2 minggu lalu
induk
melakukan
9b49b50f14

+ 80 - 2
go-speed-test/internal/core/speed_tester.go

@@ -8,6 +8,7 @@ import (
 	"net"
 	"net/http"
 	"net/url"
+	"regexp"
 	"strconv"
 	"strings"
 	"sync"
@@ -76,6 +77,16 @@ func (st *SpeedTester) TestNode(node database.Node) (*database.TestResult, error
 		"port": node.Port,
 	})
 
+	// 验证节点配置的有效性
+	if err := st.validateNode(node); err != nil {
+		result.ErrorMessage = fmt.Sprintf("节点配置无效: %v", err)
+		applogger.Warn("节点配置无效", map[string]interface{}{
+			"node":  node.Name,
+			"error": err.Error(),
+		})
+		return result, nil
+	}
+
 	// 测试连接性和速度
 	speedResult, err := st.testNodeSpeed(node)
 	if err != nil {
@@ -132,6 +143,70 @@ func (st *SpeedTester) TestNode(node database.Node) (*database.TestResult, error
 	return result, nil
 }
 
+// 验证节点配置的有效性
+func (st *SpeedTester) validateNode(node database.Node) error {
+	// 检查必需的字段
+	if node.Name == "" {
+		return fmt.Errorf("节点名称不能为空")
+	}
+	
+	if node.Type == "" {
+		return fmt.Errorf("节点类型不能为空")
+	}
+	
+	if node.Server == "" {
+		return fmt.Errorf("服务器地址不能为空")
+	}
+	
+	if node.Port <= 0 || node.Port > 65535 {
+		return fmt.Errorf("端口号无效: %d", node.Port)
+	}
+
+	// 验证server字段是否为有效的域名或IP地址
+	server := strings.TrimSpace(node.Server)
+	
+	// 只检查明显无效的情况,而不是过于严格的验证
+	// 检查是否包含中文字符(通常表示这是描述而不是有效地址)
+	for _, r := range server {
+		if r >= 0x4e00 && r <= 0x9fff {
+			return fmt.Errorf("服务器地址包含中文字符,无效: %s", server)
+		}
+	}
+	
+	// 检查是否是URL格式(不应该作为server地址)
+	if strings.HasPrefix(server, "http://") || strings.HasPrefix(server, "https://") {
+		return fmt.Errorf("服务器地址不能是URL格式: %s", server)
+	}
+	
+	// 检查是否包含明显的无效字符(如空格、特殊符号等)
+	invalidChars := regexp.MustCompile(`[<>:"\\|?*]`)
+	if invalidChars.MatchString(server) {
+		return fmt.Errorf("服务器地址包含无效字符: %s", server)
+	}
+	
+	// 检查是否为空或只包含空白字符
+	if server == "" {
+		return fmt.Errorf("服务器地址不能为空")
+	}
+
+	// 对于SS/SSR节点,检查密码
+	if (node.Type == "ss" || node.Type == "ssr") && node.Password == "" {
+		return fmt.Errorf("SS/SSR节点必须设置密码")
+	}
+
+	// 对于Vmess节点,检查UUID
+	if node.Type == "vmess" && node.UUID == "" {
+		return fmt.Errorf("Vmess节点必须设置UUID")
+	}
+
+	// 对于Trojan节点,检查密码
+	if node.Type == "trojan" && node.Password == "" {
+		return fmt.Errorf("Trojan节点必须设置密码")
+	}
+
+	return nil
+}
+
 // 批量测试节点
 func (st *SpeedTester) TestNodes(nodes []database.Node) []*database.TestResult {
 	var results []*database.TestResult
@@ -319,8 +394,11 @@ func (st *SpeedTester) testSOCKS5Proxy(node database.Node) (*SpeedTestResult, er
 func (st *SpeedTester) testAdvancedProxy(node database.Node) (*SpeedTestResult, error) {
 	result := &SpeedTestResult{}
 
+	// 对于高级代理,我们需要通过本地Clash代理来测试
+	// 但首先需要验证本地代理是否可用,以及是否真的配置了这个节点
+
 	// 对于高级代理,我们尝试通过本地代理端口进行测试
-	// 这需要代理客户端(如Clash)在本地运行并提供HTTP代理服务
+	// 如果本地代理不可用,会在测试过程中失败,这是正常的
 
 	// 尝试常见的本地代理端口
 	localProxyPorts := []int{7890, 7891, 1080, 8080, 8118}
@@ -361,7 +439,7 @@ func (st *SpeedTester) testAdvancedProxy(node database.Node) (*SpeedTestResult,
 				result.UploadSpeed = uploadSpeed
 			}
 
-			logger.Info("通过本地代理测试成功", map[string]interface{}{
+			applogger.Info("通过本地代理测试成功", map[string]interface{}{
 				"node": node.Name,
 				"port": port,
 			})

+ 64 - 7
src/core/notifier.js

@@ -6,6 +6,8 @@ class TelegramNotifier {
   constructor() {
     this.botToken = process.env.TELEGRAM_BOT_TOKEN;
     this.chatId = process.env.TELEGRAM_CHAT_ID;
+    this.lastMessageTime = 0; // 用于限流控制
+    this.minInterval = 1000; // 最小发送间隔(毫秒)
     
     if (!this.botToken || !this.chatId) {
       logger.warn('Telegram配置不完整,通知功能将被禁用');
@@ -14,7 +16,13 @@ class TelegramNotifier {
     }
 
     try {
-      this.bot = new TelegramBot(this.botToken, { polling: false });
+      this.bot = new TelegramBot(this.botToken, { 
+        polling: false,
+        request: {
+          timeout: 10000, // 10秒超时
+          connect_timeout: 10000
+        }
+      });
       this.enabled = true;
       logger.info('Telegram通知器初始化成功');
     } catch (error) {
@@ -101,10 +109,18 @@ class TelegramNotifier {
   }
 
   /**
-   * 发送通知
+   * 发送通知(带重试机制)
    */
   async sendNotification(notificationData) {
     try {
+      // 限流控制:确保发送间隔
+      const now = Date.now();
+      const timeSinceLastMessage = now - this.lastMessageTime;
+      if (timeSinceLastMessage < this.minInterval) {
+        const waitTime = this.minInterval - timeSinceLastMessage;
+        await new Promise(resolve => setTimeout(resolve, waitTime));
+      }
+
       // 将metadata转换为字符串,确保是字符串类型
       const metadataString = typeof notificationData.metadata === 'object' 
         ? JSON.stringify(notificationData.metadata) 
@@ -118,13 +134,10 @@ class TelegramNotifier {
       // 保存通知记录
       const notification = await Notification.create(notificationDataForDB);
 
-      // 发送Telegram消息
+      // 发送Telegram消息(带重试)
       const fullMessage = `*${notificationData.title}*\n\n${notificationData.message}`;
       
-      const sentMessage = await this.bot.sendMessage(this.chatId, fullMessage, {
-        parse_mode: 'Markdown',
-        disable_web_page_preview: true
-      });
+      const sentMessage = await this.sendMessageWithRetry(fullMessage);
 
       // 更新通知状态
       await notification.update({
@@ -132,6 +145,8 @@ class TelegramNotifier {
         sentTime: new Date()
       });
 
+      this.lastMessageTime = Date.now();
+
       logger.info('Telegram通知发送成功', {
         notificationId: notification.id,
         messageId: sentMessage.message_id
@@ -155,6 +170,48 @@ class TelegramNotifier {
     }
   }
 
+  /**
+   * 带重试的消息发送
+   */
+  async sendMessageWithRetry(message, maxRetries = 3) {
+    for (let attempt = 1; attempt <= maxRetries; attempt++) {
+      try {
+        const sentMessage = await this.bot.sendMessage(this.chatId, message, {
+          parse_mode: 'Markdown',
+          disable_web_page_preview: true
+        });
+        return sentMessage;
+      } catch (error) {
+        if (attempt === maxRetries) {
+          throw error;
+        }
+        
+        // 如果是限流错误,等待更长时间
+        if (error.message.includes('429')) {
+          const retryAfter = this.extractRetryAfter(error.message) || 30;
+          logger.warn(`Telegram API限流,等待 ${retryAfter} 秒后重试`, { attempt });
+          await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
+        } else {
+          // 其他错误,等待较短时间后重试
+          const waitTime = attempt * 2000; // 递增等待时间
+          logger.warn(`Telegram发送失败,${waitTime}ms后重试`, { 
+            attempt, 
+            error: error.message 
+          });
+          await new Promise(resolve => setTimeout(resolve, waitTime));
+        }
+      }
+    }
+  }
+
+  /**
+   * 从错误消息中提取重试时间
+   */
+  extractRetryAfter(errorMessage) {
+    const match = errorMessage.match(/retry after (\d+)/);
+    return match ? parseInt(match[1]) : null;
+  }
+
   /**
    * 格式化故障消息
    */

+ 113 - 18
src/core/speedTester.js

@@ -4,6 +4,7 @@ const { SocksProxyAgent } = require('socks-proxy-agent');
 const { HttpProxyAgent } = require('http-proxy-agent');
 const logger = require('../utils/logger');
 const { TestResult } = require('../models');
+const net = require('net');
 
 class SpeedTester {
   constructor() {
@@ -17,7 +18,37 @@ class SpeedTester {
   }
 
   /**
-   * 测试单个节点连通性
+   * TCP端口连通性检测
+   */
+  async testTcpConnectivity(server, port, timeout = 5000) {
+    return new Promise((resolve) => {
+      const socket = new net.Socket();
+      let isConnected = false;
+
+      socket.setTimeout(timeout);
+
+      socket.on('connect', () => {
+        isConnected = true;
+        socket.destroy();
+        resolve({ success: true });
+      });
+
+      socket.on('timeout', () => {
+        socket.destroy();
+        if (!isConnected) resolve({ success: false, error: '连接超时' });
+      });
+
+      socket.on('error', (err) => {
+        socket.destroy();
+        if (!isConnected) resolve({ success: false, error: err.message });
+      });
+
+      socket.connect(port, server);
+    });
+  }
+
+  /**
+   * 测试单个节点连通性(只做TCP端口探测)
    */
   async testNode(node) {
     const startTime = Date.now();
@@ -39,28 +70,24 @@ class SpeedTester {
     try {
       logger.info(`开始测试节点连通性: ${node.name} (${node.type})`);
 
-      // 创建代理配置
-      const proxyConfig = this.createProxyConfig(node);
-      if (!proxyConfig) {
-        throw new Error(`不支持的代理类型: ${node.type}`);
+      // 验证节点配置的有效性
+      const validationResult = this.validateNode(node);
+      if (!validationResult.isValid) {
+        throw new Error(validationResult.error);
       }
 
-      // 只测试基本连通性
-      const connectivityResult = await this.testBasicConnectivity(node, proxyConfig);
-      testResult.isSuccess = connectivityResult.success;
-      testResult.latency = connectivityResult.latency;
-      testResult.testUrl = connectivityResult.url;
-      testResult.ipAddress = connectivityResult.ipAddress;
-      testResult.location = connectivityResult.location;
+      // 直接检测 server:port 的 TCP 连通性
+      const tcpResult = await this.testTcpConnectivity(node.server, node.port, this.timeout);
+      testResult.isSuccess = tcpResult.success;
+      testResult.errorMessage = tcpResult.error || null;
 
       testResult.testDuration = Date.now() - startTime;
-      
+
       if (testResult.isSuccess) {
-        logger.info(`✅ 节点连通性测试成功: ${node.name} - 延迟: ${testResult.latency}ms - IP: ${testResult.ipAddress}`);
+        logger.info(`✅ 节点TCP连通性测试成功: ${node.name}`);
       } else {
-        logger.warn(`❌ 节点连通性测试失败: ${node.name} - ${testResult.errorMessage || '连接超时'}`);
+        logger.warn(`❌ 节点TCP连通性测试失败: ${node.name} - ${testResult.errorMessage || '连接超时'}`);
       }
-
     } catch (error) {
       testResult.errorMessage = error.message;
       testResult.testDuration = Date.now() - startTime;
@@ -73,6 +100,69 @@ class SpeedTester {
     return testResult;
   }
 
+  /**
+   * 验证节点配置的有效性
+   */
+  validateNode(node) {
+    // 检查必需的字段
+    if (!node.name) {
+      return { isValid: false, error: '节点名称不能为空' };
+    }
+    
+    if (!node.type) {
+      return { isValid: false, error: '节点类型不能为空' };
+    }
+    
+    if (!node.server) {
+      return { isValid: false, error: '服务器地址不能为空' };
+    }
+    
+    if (!node.port || node.port <= 0 || node.port > 65535) {
+      return { isValid: false, error: '端口号无效' };
+    }
+
+    // 验证server字段是否为有效的域名或IP地址
+    const server = node.server.trim();
+    
+    // 只检查明显无效的情况,而不是过于严格的验证
+    // 检查是否包含中文字符(通常表示这是描述而不是有效地址)
+    if (/[\u4e00-\u9fa5]/.test(server)) {
+      return { isValid: false, error: `服务器地址包含中文字符,无效: ${server}` };
+    }
+    
+    // 检查是否是URL格式(不应该作为server地址)
+    if (server.startsWith('http://') || server.startsWith('https://')) {
+      return { isValid: false, error: `服务器地址不能是URL格式: ${server}` };
+    }
+    
+    // 检查是否包含明显的无效字符(如空格、特殊符号等)
+    if (/[<>:"\\|?*]/.test(server)) {
+      return { isValid: false, error: `服务器地址包含无效字符: ${server}` };
+    }
+    
+    // 检查是否为空或只包含空白字符
+    if (!server || server.trim() === '') {
+      return { isValid: false, error: '服务器地址不能为空' };
+    }
+
+    // 对于SS/SSR节点,检查密码
+    if ((node.type === 'ss' || node.type === 'ssr') && !node.password) {
+      return { isValid: false, error: 'SS/SSR节点必须设置密码' };
+    }
+
+    // 对于Vmess节点,检查UUID
+    if (node.type === 'vmess' && !node.uuid) {
+      return { isValid: false, error: 'Vmess节点必须设置UUID' };
+    }
+
+    // 对于Trojan节点,检查密码
+    if (node.type === 'trojan' && !node.password) {
+      return { isValid: false, error: 'Trojan节点必须设置密码' };
+    }
+
+    return { isValid: true, error: null };
+  }
+
   /**
    * 创建代理配置
    */
@@ -229,6 +319,9 @@ class SpeedTester {
       return this.testDirectConnectivity();
     }
     
+    // 对于高级代理类型,我们仍然尝试通过本地代理测试
+    // 如果本地代理不可用,会在makeRequest中失败,这是正常的
+    
     // 尝试多个测试URL
     for (const url of this.testUrls) {
       try {
@@ -267,6 +360,8 @@ class SpeedTester {
     };
   }
 
+
+
   /**
    * 测试直连连通性
    */
@@ -381,7 +476,7 @@ class SpeedTester {
           logger.info(`通过本地Clash代理测试: ${proxyConfig.type} - ${node.name}`);
           
           // 使用本地Clash混合端口
-          const localProxyUrl = 'http://127.0.0.1:7897';
+          const localProxyUrl = 'http://127.0.0.1:7890';
           const localAgent = new HttpsProxyAgent(localProxyUrl);
           logger.debug(`使用本地Clash代理: ${node.name} - ${localProxyUrl}`);
           return localAgent;
@@ -482,4 +577,4 @@ class SpeedTester {
   }
 }
 
-module.exports = SpeedTester; 
+module.exports = SpeedTester;

+ 128 - 0
test_connection.js

@@ -0,0 +1,128 @@
+const logger = require('./src/utils/logger');
+const sequelize = require('./src/config/database');
+const TelegramNotifier = require('./src/core/notifier');
+
+async function testDatabaseConnection() {
+  console.log('🔍 测试数据库连接...');
+  
+  try {
+    console.log('正在连接数据库...');
+    await sequelize.authenticate();
+    console.log('✅ 数据库连接成功');
+    
+    // 测试查询
+    console.log('正在执行测试查询...');
+    const result = await sequelize.query('SELECT 1 as test');
+    console.log('✅ 数据库查询测试成功:', result[0]);
+    
+    // 测试模型
+    console.log('正在测试数据模型...');
+    const { Node, TestResult } = require('./src/models');
+    
+    // 测试Node模型
+    const nodeCount = await Node.count();
+    console.log('✅ Node模型测试成功,当前节点数:', nodeCount);
+    
+    // 测试TestResult模型
+    const resultCount = await TestResult.count();
+    console.log('✅ TestResult模型测试成功,当前测试结果数:', resultCount);
+    
+  } catch (error) {
+    console.error('❌ 数据库连接失败:', error.message);
+    console.error('详细错误信息:', error);
+    return false;
+  }
+  
+  return true;
+}
+
+async function testTelegramNotification() {
+  console.log('🔍 测试Telegram通知...');
+  
+  try {
+    const notifier = new TelegramNotifier();
+    
+    if (!notifier.enabled) {
+      console.log('⚠️ Telegram通知已禁用(配置不完整)');
+      return false;
+    }
+    
+    // 测试连接
+    const testResult = await notifier.testConnection();
+    console.log('✅ Telegram连接测试成功:', testResult);
+    
+    // 发送测试消息
+    await notifier.sendSystemNotification(
+      '🧪 连接测试',
+      '这是一条测试消息,用于验证Telegram通知功能是否正常工作。\n\n发送时间: ' + new Date().toLocaleString('zh-CN'),
+      { test: true, timestamp: new Date().toISOString() }
+    );
+    
+    console.log('✅ Telegram通知发送成功');
+    return true;
+    
+  } catch (error) {
+    console.error('❌ Telegram通知测试失败:', error.message);
+    console.error('详细错误信息:', error);
+    return false;
+  }
+}
+
+async function testSpeedTester() {
+  console.log('🔍 测试测速功能...');
+  
+  try {
+    const SpeedTester = require('./src/core/speedTester');
+    const speedTester = new SpeedTester();
+    
+    // 测试一个简单的TCP连接
+    const testResult = await speedTester.testTcpConnectivity('8.8.8.8', 53, 5000);
+    console.log('✅ TCP连通性测试成功:', testResult);
+    
+    return true;
+    
+  } catch (error) {
+    console.error('❌ 测速功能测试失败:', error.message);
+    console.error('详细错误信息:', error);
+    return false;
+  }
+}
+
+async function main() {
+  console.log('🚀 开始系统连接测试...\n');
+  
+  const results = {
+    database: await testDatabaseConnection(),
+    telegram: await testTelegramNotification(),
+    speedTester: await testSpeedTester()
+  };
+  
+  console.log('\n📊 测试结果汇总:');
+  console.log('数据库连接:', results.database ? '✅ 成功' : '❌ 失败');
+  console.log('Telegram通知:', results.telegram ? '✅ 成功' : '❌ 失败');
+  console.log('测速功能:', results.speedTester ? '✅ 成功' : '❌ 失败');
+  
+  const allPassed = Object.values(results).every(result => result);
+  console.log('\n🎯 总体结果:', allPassed ? '✅ 所有测试通过' : '❌ 部分测试失败');
+  
+  if (!allPassed) {
+    console.log('\n💡 建议:');
+    if (!results.database) {
+      console.log('- 检查数据库配置和网络连接');
+    }
+    if (!results.telegram) {
+      console.log('- 检查Telegram Bot Token和Chat ID配置');
+      console.log('- 确保网络可以访问Telegram API');
+    }
+    if (!results.speedTester) {
+      console.log('- 检查测速相关依赖包是否正确安装');
+    }
+  }
+  
+  process.exit(allPassed ? 0 : 1);
+}
+
+main().catch(error => {
+  console.error('❌ 测试过程中发生错误:', error);
+  process.exit(1);
+}); 

+ 152 - 0
test_invalid_nodes.js

@@ -0,0 +1,152 @@
+const SpeedTester = require('./src/core/speedTester');
+const logger = require('./src/utils/logger');
+
+// 测试无效节点
+const invalidNodes = [
+  {
+    id: 1,
+    name: '最新官网:so.xfxssr.me',
+    type: 'ss',
+    server: '最新官网:so.xfxssr.me',
+    port: 1080,
+    password: '235a4cf6-9663-467d-a66f-9bf7cbd31f16',
+    cipher: 'chacha20-ietf-poly1305'
+  },
+  {
+    id: 2,
+    name: 'x 代表使用倍率',
+    type: 'ss',
+    server: 'x 代表使用倍率',
+    port: 1080,
+    password: '235a4cf6-9663-467d-a66f-9bf7cbd31f16',
+    cipher: 'chacha20-ietf-poly1305'
+  },
+  {
+    id: 3,
+    name: '用不了,请更新订阅',
+    type: 'ss',
+    server: '用不了,请更新订阅',
+    port: 1080,
+    password: '235a4cf6-9663-467d-a66f-9bf7cbd31f16',
+    cipher: 'chacha20-ietf-poly1305'
+  },
+  {
+    id: 4,
+    name: '永久地址请查看活动公告',
+    type: 'ss',
+    server: 'https://sulinkcloud.github.io/',
+    port: 55626,
+    password: '235a4cf6-9663-467d-a66f-9bf7cbd31f16',
+    cipher: 'chacha20-ietf-poly1305'
+  }
+];
+
+// 测试有效节点作为对比
+const validNodes = [
+  {
+    id: 5,
+    name: '香港01',
+    type: 'ss',
+    server: 'gya.tangu.win',
+    port: 19630,
+    password: '235a4cf6-9663-467d-a66f-9bf7cbd31f16',
+    cipher: 'chacha20-ietf-poly1305'
+  },
+  {
+    id: 6,
+    name: '香港04',
+    type: 'ss',
+    server: 'gya.tangu.win',
+    port: 15635,
+    password: '235a4cf6-9663-467d-a66f-9bf7cbd31f16',
+    cipher: 'chacha20-ietf-poly1305'
+  },
+  {
+    id: 7,
+    name: '美国01',
+    type: 'ss',
+    server: 'gya.tangu.win',
+    port: 15532,
+    password: '235a4cf6-9663-467d-a66f-9bf7cbd31f16',
+    cipher: 'chacha20-ietf-poly1305'
+  },
+  {
+    id: 8,
+    name: '香港04-备用',
+    type: 'ss',
+    server: 'qqa813.198139.xyz',
+    port: 26642,
+    password: '235a4cf6-9663-467d-a66f-9bf7cbd31f16',
+    cipher: 'chacha20-ietf-poly1305'
+  }
+];
+
+async function testNodes() {
+  const speedTester = new SpeedTester();
+  
+  console.log('=== 测试无效节点 ===');
+  let invalidSuccessCount = 0;
+  let invalidFailCount = 0;
+  
+  for (const node of invalidNodes) {
+    console.log(`\n测试节点: ${node.name}`);
+    console.log(`服务器: ${node.server}`);
+    console.log(`类型: ${node.type}`);
+    
+    try {
+      const result = await speedTester.testNode(node);
+      if (result.isSuccess) {
+        console.log(`结果: ✅ 成功 (这不应该发生!)`);
+        invalidSuccessCount++;
+      } else {
+        console.log(`结果: ❌ 失败 (正确)`);
+        console.log(`错误: ${result.errorMessage}`);
+        invalidFailCount++;
+      }
+    } catch (error) {
+      console.log(`异常: ${error.message}`);
+      invalidFailCount++;
+    }
+  }
+  
+  console.log('\n=== 测试有效节点 ===');
+  let validSuccessCount = 0;
+  let validFailCount = 0;
+  
+  for (const node of validNodes) {
+    console.log(`\n测试节点: ${node.name}`);
+    console.log(`服务器: ${node.server}`);
+    console.log(`类型: ${node.type}`);
+    
+    try {
+      const result = await speedTester.testNode(node);
+      if (result.isSuccess) {
+        console.log(`结果: ✅ 成功`);
+        validSuccessCount++;
+      } else {
+        console.log(`结果: ❌ 失败`);
+        console.log(`错误: ${result.errorMessage}`);
+        validFailCount++;
+      }
+    } catch (error) {
+      console.log(`异常: ${error.message}`);
+      validFailCount++;
+    }
+  }
+  
+  console.log('\n=== 测试总结 ===');
+  console.log(`无效节点: ${invalidFailCount}/${invalidNodes.length} 正确失败`);
+  console.log(`有效节点: ${validSuccessCount}/${validNodes.length} 成功连接`);
+  console.log(`总成功率: ${validSuccessCount}/${invalidNodes.length + validNodes.length} (${((validSuccessCount/(invalidNodes.length + validNodes.length))*100).toFixed(1)}%)`);
+  
+  if (invalidSuccessCount === 0 && validSuccessCount > 0) {
+    console.log('✅ 修复成功!无效节点被正确识别,有效节点可以正常测试');
+  } else if (invalidSuccessCount > 0) {
+    console.log('❌ 修复不完整,仍有无效节点显示为成功');
+  } else if (validSuccessCount === 0) {
+    console.log('❌ 修复过于严格,有效节点也被阻止了');
+  }
+}
+
+// 运行测试
+testNodes().catch(console.error);