Web浏览器客户端检测网站网络健康(代码)

健康检查思路

***{XXX网站}整体排错思路***

  1. 常见网站连通信测试

1)仅国内百度可访问通表明代理错误

2)仅{XXX网站}无法访问表明代理DNS服务器没有被收集,或个人代理没有设置

  1. 基础网络信息

1)获取浏览器网络信息

2)获取系统信息

3)获取代理信息

  1. 站点连通性检测

1)查看链接CDN地址及IP

2){XXX网站} 80、443 连通信时长

  1. 资源连通性检测

1)访问指定资源,检查连通信时长,时间过长界面会产生搓板情况

  1. 网络抖动检测

1)访问10次同一资源,查看平均访问差异计算网络质量

  1. 安全与证书检测

1)手动查看证书,如果非*. {XXX网站}表明代理存在问题

2)严重时需要用户提供路由跟踪信息排查

Windows用户:打开命令提示符,输入 tracert {XXX网站}

Mac用户:打开终端,输入 traceroute {XXX网站}

  1. 如需{XXX网站}专业人员排查,提供检查日志

代码

文件名 {XXX网站}-check.html

这里已 super.boluobao.ai 为测试,因为他是外网地址

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>super.boluobao.ai 网络连通性专业自检工具</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: #ffffff;
            border-radius: 16px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
            overflow: hidden;
        }
        
        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 32px 40px;
        }
        
        .header h1 {
            font-size: 28px;
            font-weight: 600;
            margin-bottom: 8px;
        }
        
        .header p {
            opacity: 0.9;
            font-size: 16px;
        }
        
        .content {
            padding: 32px 40px;
        }
        
        .control-panel {
            display: flex;
            gap: 12px;
            margin-bottom: 24px;
            flex-wrap: wrap;
        }
        
        .btn {
            padding: 12px 24px;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 500;
            transition: all 0.3s ease;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        
        .btn-primary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
        }
        
        .btn-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 20px rgba(102, 126, 234, 0.5);
        }
        
        .btn-primary:disabled {
            background: #a0aec0;
            cursor: not-allowed;
            transform: none;
            box-shadow: none;
        }
        
        .btn-secondary {
            background: #f7fafc;
            color: #4a5568;
            border: 1px solid #e2e8f0;
        }
        
        .btn-secondary:hover {
            background: #edf2f7;
        }
        
        .progress-bar {
            width: 100%;
            height: 8px;
            background: #e2e8f0;
            border-radius: 4px;
            overflow: hidden;
            margin-bottom: 32px;
        }
        
        .progress-fill {
            height: 100%;
            background: linear-gradient(90deg, #667eea, #764ba2);
            width: 0%;
            transition: width 0.5s ease;
            border-radius: 4px;
        }
        
        .section {
            background: #ffffff;
            border: 1px solid #e2e8f0;
            border-radius: 12px;
            margin-bottom: 24px;
            overflow: hidden;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
        }
        
        .section-header {
            background: #f7fafc;
            padding: 20px 24px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-bottom: 1px solid #e2e8f0;
        }
        
        .section-title {
            font-size: 18px;
            font-weight: 600;
            color: #2d3748;
        }
        
        .section-status {
            padding: 6px 16px;
            border-radius: 20px;
            font-size: 14px;
            font-weight: 500;
        }
        
        .status-success {
            background: #c6f6d5;
            color: #22543d;
        }
        
        .status-failed {
            background: #fed7d7;
            color: #742a2a;
        }
        
        .status-warning {
            background: #feebc8;
            color: #744210;
        }
        
        .status-info {
            background: #bee3f8;
            color: #2b6cb0;
        }
        
        .status-pending {
            background: #e2e8f0;
            color: #718096;
        }
        
        .section-content {
            padding: 24px;
        }
        
        .info-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
            gap: 20px;
        }
        
        .info-card {
            background: #f7fafc;
            border-radius: 10px;
            padding: 20px;
            border-left: 4px solid #667eea;
        }
        
        .info-label {
            font-size: 13px;
            color: #718096;
            margin-bottom: 6px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }
        
        .info-value {
            font-size: 16px;
            font-weight: 500;
            color: #2d3748;
            word-break: break-all;
        }
        
        .test-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 16px 0;
            border-bottom: 1px solid #f0f0f0;
        }
        
        .test-item:last-child {
            border-bottom: none;
        }
        
        .test-name {
            font-size: 15px;
            color: #2d3748;
            font-weight: 500;
        }
        
        .test-result {
            display: flex;
            align-items: center;
            gap: 12px;
        }
        
        .test-status {
            padding: 4px 12px;
            border-radius: 16px;
            font-size: 13px;
            font-weight: 500;
        }
        
        .test-details {
            font-size: 13px;
            color: #718096;
        }
        
        .table-container {
            overflow-x: auto;
            margin-top: 16px;
        }
        
        table {
            width: 100%;
            border-collapse: collapse;
            font-size: 14px;
        }
        
        th, td {
            padding: 12px 16px;
            text-align: left;
            border-bottom: 1px solid #e2e8f0;
        }
        
        th {
            background: #f7fafc;
            font-weight: 600;
            color: #4a5568;
        }
        
        tr:hover {
            background: #f7fafc;
        }
        
        .chart-container {
            height: 300px;
            margin-top: 24px;
            position: relative;
        }
        
        .summary {
            background: linear-gradient(135deg, #fff5f5 0%, #fed7d7 100%);
            border: 1px solid #fc8181;
            border-radius: 12px;
            padding: 24px;
            margin-top: 24px;
        }
        
        .summary h3 {
            font-size: 18px;
            color: #742a2a;
            margin-bottom: 16px;
        }
        
        .summary-item {
            margin-bottom: 8px;
            font-size: 14px;
            color: #2d3748;
        }
        
        .summary-item strong {
            color: #742a2a;
        }
        
        .troubleshooting {
            background: linear-gradient(135deg, #fffaf0 0%, #feebc8 100%);
            border: 1px solid #fbd38d;
            border-radius: 12px;
            padding: 24px;
            margin-top: 16px;
        }
        
        .troubleshooting h3 {
            font-size: 18px;
            color: #744210;
            margin-bottom: 16px;
        }
        
        .troubleshooting ul {
            padding-left: 20px;
            font-size: 14px;
            color: #2d3748;
        }
        
        .troubleshooting li {
            margin-bottom: 6px;
        }
        
        .log-container {
            background: #1a202c;
            color: #e2e8f0;
            padding: 20px;
            border-radius: 12px;
            font-family: 'Consolas', 'Monaco', monospace;
            font-size: 13px;
            height: 250px;
            overflow-y: auto;
            white-space: pre-wrap;
            word-wrap: break-word;
            margin-top: 24px;
        }
        
        .log-info { color: #90cdf4; }
        .log-success { color: #9ae6b4; }
        .log-error { color: #fc8181; }
        .log-warning { color: #f6e05e; }
        
        /* Chrome DevTools Network Panel Styles */
        .network-panel {
            background: #f8f9fa;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            overflow: hidden;
        }
        
        .network-timeline {
            background: #ffffff;
            border-bottom: 1px solid #e0e0e0;
            padding: 8px 0;
            min-width: 1200px;
        }
        
        .timeline-header {
            display: flex;
            justify-content: space-between;
            padding: 0 12px;
            font-size: 11px;
            color: #5f6368;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        }
        
        .timeline-label {
            flex: 1;
            text-align: center;
        }
        
        .timeline-grid {
            height: 8px;
            background: repeating-linear-gradient(
                90deg,
                #e0e0e0,
                #e0e0e0 1px,
                transparent 1px,
                transparent 14.28%
            );
            margin-top: 4px;
        }
        
        .network-timeline-container {
            overflow-x: auto;
            background: #ffffff;
        }
        
        .network-table-container {
            overflow-x: auto;
            background: #ffffff;
        }
        
        #networkTable {
            min-width: 1200px;
            border-collapse: collapse;
            font-size: 12px;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        }
        
        #networkTable th,
        #networkTable td {
            padding: 8px 12px;
            text-align: left;
            border-bottom: 1px solid #f0f0f0;
            white-space: nowrap;
        }
        
        #networkTable th {
            background: #f5f5f5;
            font-weight: 500;
            color: #5f6368;
            position: sticky;
            top: 0;
            z-index: 10;
        }
        
        #networkTable tbody tr:hover {
            background: #f0f7ff;
        }
        
        .col-name { min-width: 200px; }
        .col-status { min-width: 80px; }
        .col-type { min-width: 100px; }
        .col-initiator { min-width: 100px; }
        .col-time { min-width: 80px; }
        .col-waterfall { min-width: 400px; }
        
        .status-2xx { color: #1a73e8; }
        .status-3xx { color: #f9ab00; }
        .status-4xx { color: #d93025; }
        .status-5xx { color: #d93025; }
        .status-ok { color: #1a73e8; }
        .status-error { color: #d93025; }
        
        .waterfall-cell {
            position: relative;
            height: 16px;
        }
        
        .waterfall-bar {
            position: absolute;
            top: 4px;
            height: 8px;
            border-radius: 2px;
        }
        
        .waterfall-bar.html { background: #4285f4; }
        .waterfall-bar.css { background: #34a853; }
        .waterfall-bar.js { background: #fbbc05; }
        .waterfall-bar.image { background: #ea4335; }
        .waterfall-bar.font { background: #9c27b0; }
        .waterfall-bar.other { background: #607d8b; }
        
        .network-stats {
            display: flex;
            gap: 24px;
            padding: 12px;
            background: #f5f5f5;
            border-top: 1px solid #e0e0e0;
            font-size: 12px;
            color: #5f6368;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        }
        
        /* Log Controls */
        .log-controls {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-top: 24px;
            margin-bottom: 8px;
        }
        
        .log-controls h3 {
            font-size: 16px;
            color: #2d3748;
        }
        
        .log-buttons {
            display: flex;
            gap: 8px;
        }
        
        .log-buttons .btn {
            padding: 6px 12px;
            font-size: 13px;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>super.boluobao.ai 网络连通性专业自检工具</h1>
            <p>全面检测与 https://super.boluobao.ai 的网络连接问题,精确定位故障原因</p>
        </div>
        
        <div class="content">
            <div class="control-panel">
                <button id="startTestBtn" class="btn btn-primary">
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <polygon points="5 3 19 12 5 21 5 3"></polygon>
                    </svg>
                    开始全面检测
                </button>
                <button id="stopTestBtn" class="btn btn-secondary" disabled>
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <rect x="6" y="6" width="12" height="12"></rect>
                    </svg>
                    停止检测
                </button>
                <button id="clearLogBtn" class="btn btn-secondary">
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <polyline points="3 6 5 6 21 6"></polyline>
                        <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                    </svg>
                    清除日志
                </button>
            </div>
            
            <div class="progress-bar">
                <div id="progressFill" class="progress-fill"></div>
            </div>
            
            <!-- 1. 常见网站连通信测试 -->
            <div class="section">
                <div class="section-header">
                    <h2 class="section-title">1. 常见网站连通信测试</h2>
                    <span id="commonWebsitesStatus" class="section-status status-pending">待检测</span>
                </div>
                <div class="section-content">
                    <div style="margin-bottom: 16px;">
                        <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; margin-bottom: 24px;">
                            <div id="test-boluobaoAI" class="website-test-item" style="background: #f8f9fa; border: 2px solid #e9ecef; border-radius: 12px; padding: 16px; text-align: center; cursor: pointer; transition: all 0.3s ease;">
                                <div style="font-size: 32px; margin-bottom: 8px;">🌐</div>
                                <div style="font-weight: 600; color: #2d3748; margin-bottom: 4px;">boluobaoAI</div>
                                <div style="font-size: 12px; color: #718096;" id="test-boluobaoAI-status">待检测</div>
                            </div>
                            <div id="test-Baidu" class="website-test-item" style="background: #f8f9fa; border: 2px solid #e9ecef; border-radius: 12px; padding: 16px; text-align: center; cursor: pointer; transition: all 0.3s ease;">
                                <div style="font-size: 32px; margin-bottom: 8px;">🔍</div>
                                <div style="font-weight: 600; color: #2d3748; margin-bottom: 4px;">百度</div>
                                <div style="font-size: 12px; color: #718096;" id="test-Baidu-status">待检测</div>
                            </div>
                            <div id="test-Google" class="website-test-item" style="background: #f8f9fa; border: 2px solid #e9ecef; border-radius: 12px; padding: 16px; text-align: center; cursor: pointer; transition: all 0.3s ease;">
                                <div style="font-size: 32px; margin-bottom: 8px;">🔍</div>
                                <div style="font-weight: 600; color: #2d3748; margin-bottom: 4px;">Google</div>
                                <div style="font-size: 12px; color: #718096;" id="test-Google-status">待检测</div>
                            </div>
                            <div id="test-Facebook" class="website-test-item" style="background: #f8f9fa; border: 2px solid #e9ecef; border-radius: 12px; padding: 16px; text-align: center; cursor: pointer; transition: all 0.3s ease;">
                                <div style="font-size: 32px; margin-bottom: 8px;">📘</div>
                                <div style="font-weight: 600; color: #2d3748; margin-bottom: 4px;">Facebook</div>
                                <div style="font-size: 12px; color: #718096;" id="test-Facebook-status">待检测</div>
                            </div>
                            <div id="test-X" class="website-test-item" style="background: #f8f9fa; border: 2px solid #e9ecef; border-radius: 12px; padding: 16px; text-align: center; cursor: pointer; transition: all 0.3s ease;">
                                <div style="font-size: 32px; margin-bottom: 8px;">𝕏</div>
                                <div style="font-weight: 600; color: #2d3748; margin-bottom: 4px;">X</div>
                                <div style="font-size: 12px; color: #718096;" id="test-X-status">待检测</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            
            <!-- 2. 基础网络信息 -->
            <div class="section">
                <div class="section-header">
                    <h2 class="section-title">2. 基础网络信息</h2>
                    <span id="basicStatus" class="section-status status-pending">待检测</span>
                </div>
                <div class="section-content">
                    <div class="info-grid">
                        <div class="info-card">
                            <div class="info-label">浏览器访问状态</div>
                            <div class="info-value" id="browserStatus">待检测</div>
                        </div>
                        <div class="info-card">
                            <div class="info-label">出口IP地址</div>
                            <div class="info-value" id="exitIp">待检测</div>
                        </div>
						<!-- 新增IP完整信息(中文展示) -->
                        <div class="info-card">
                            <div class="info-label">出口IP所在城市</div>
                            <div class="info-value" id="ipCity">待检测</div>
                        </div>
                        <div class="info-card">
                            <div class="info-label">出口IP所在地区</div>
                            <div class="info-value" id="ipRegion">待检测</div>
                        </div>
                        <div class="info-card">
                            <div class="info-label">出口IP所在国家</div>
                            <div class="info-value" id="ipCountry">待检测</div>
                        </div>
                        <div class="info-card">
                            <div class="info-label">出口IP经纬度</div>
                            <div class="info-value" id="ipLoc">待检测</div>
                        </div>
                        <div class="info-card">
                            <div class="info-label">出口IP邮编</div>
                            <div class="info-value" id="ipPostal">待检测</div>
                        </div>
                        <div class="info-card">
                            <div class="info-label">出口IP时区</div>
                            <div class="info-value" id="ipTimezone">待检测</div>
                        </div>
                        <div class="info-card">
                            <div class="info-label">系统时区</div>
                            <div class="info-value" id="systemTimezone">待检测</div>
                        </div>
                        <div class="info-card">
                            <div class="info-label">系统时间</div>
                            <div class="info-value" id="systemTime">待检测</div>
                        </div>
                        <div class="info-card">
                            <div class="info-label">网络运营商</div>
                            <div class="info-value" id="isp">待检测</div>
                        </div>
                        <div class="info-card">
                            <div class="info-label">网络类型</div>
                            <div class="info-value" id="networkType">待检测</div>
                        </div>
                    </div>
                </div>
            </div>
            
            <!-- 3. 站点连通性检测 -->
            <div class="section">
                <div class="section-header">
                    <h2 class="section-title">3. 站点连通性检测</h2>
                    <span id="siteStatus" class="section-status status-pending">待检测</span>
                </div>
                <div class="section-content">
                    <div class="test-item">
                        <div class="test-name">站点IP地址 (super.boluobao.ai)</div>
                        <div class="test-result">
                            <span id="siteIpResult" class="test-status status-pending">待检测</span>
                            <span id="siteIpDetails" class="test-details"></span>
                        </div>
                    </div>
                    <div class="test-item">
                        <div class="test-name">HTTP 80端口连通性</div>
                        <div class="test-result">
                            <span id="http80Result" class="test-status status-pending">待检测</span>
                            <span id="http80Details" class="test-details"></span>
                        </div>
                    </div>
                    <div class="test-item">
                        <div class="test-name">HTTPS 443端口连通性</div>
                        <div class="test-result">
                            <span id="https443Result" class="test-status status-pending">待检测</span>
                            <span id="https443Details" class="test-details"></span>
                        </div>
                    </div>
                </div>
            </div>
            
            <!-- 4. 资源连通性检测 -->
            <div class="section">
                <div class="section-header">
                    <h2 class="section-title">4. 资源连通性检测</h2>
                    <span id="resourceStatus" class="section-status status-pending">待检测</span>
                </div>
                <div class="section-content">
                    <!-- 资源列表说明 -->
                    <div style="background: #d4edda; border: 1px solid #c3e6cb; border-radius: 8px; padding: 16px; margin-bottom: 16px;">
                        <h3 style="color: #155724; margin-bottom: 8px; font-size: 14px;">✅ 指定资源检测</h3>
                        <p style="color: #155724; font-size: 13px; line-height: 1.6;">
                            将检测以下 <strong>11个关键资源</strong> 的连通性:<br>
                            - 首页及中文页面 (3个)<br>
                            - 图片资源 (1个)<br>
                            - API接口 (7个)
                        </p>
                    </div>
                    
                    <!-- Chrome DevTools 风格的Network面板 -->
                    <div class="network-panel">
                        <!-- 时间轴 -->
                        <div class="network-timeline-container">
                            <div class="network-timeline" id="networkTimeline">
                                <div class="timeline-header">
                                    <div class="timeline-label">0 ms</div>
                                    <div class="timeline-label">5000 ms</div>
                                    <div class="timeline-label">10000 ms</div>
                                    <div class="timeline-label">15000 ms</div>
                                    <div class="timeline-label">20000 ms</div>
                                    <div class="timeline-label">25000 ms</div>
                                    <div class="timeline-label">30000 ms</div>
                                </div>
                                <div class="timeline-grid"></div>
                            </div>
                        </div>
                        
                        <!-- 网络请求表格 -->
                        <div class="network-table-container">
                            <table id="networkTable">
                                <thead>
                                    <tr>
                                        <th class="col-name">名称</th>
                                        <th class="col-status">状态</th>
                                        <th class="col-type">类型</th>
                                        <th class="col-initiator">启动器</th>
                                        <th class="col-time">时间</th>
                                        <th class="col-waterfall">瀑布图</th>
                                    </tr>
                                </thead>
                                <tbody id="networkTableBody">
                                    <tr>
                                        <td colspan="6" style="text-align: center; color: #718096; padding: 20px;">等待检测...</td>
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                        

                        
                        <!-- 统计信息 -->
                        <div class="network-stats" id="networkStats" style="display: none;">
                            <span id="requestCount">0 个请求</span>
                            <span id="loadTime">完成时间 0 ms</span>
                        </div>
                    </div>
                </div>
            </div>
            
            <!-- 5. 网络抖动检测 -->
            <div class="section">
                <div class="section-header">
                    <h2 class="section-title">5. 网络抖动检测</h2>
                    <span id="latencyStatus" class="section-status status-pending">待检测</span>
                </div>
                <div class="section-content">
                    <div style="margin-bottom: 16px;">
                        <!-- 检测1:服务器响应延迟 -->
                        <div class="test-item" style="border-bottom: 1px solid #e2e8f0; padding: 12px 0;">
                            <div style="display: flex; justify-content: space-between; align-items: center;">
                                <div>
                                    <div style="font-weight: 600; color: #2d3748; font-size: 14px;">服务器响应延迟</div>
                                    <div style="font-size: 12px; color: #718096; margin-top: 4px;">通过多次请求测量平均响应时间</div>
                                </div>
                                <div style="display: flex; align-items: center; gap: 12px;">
                                    <span id="latency1Status" class="test-status status-pending">待检测</span>
                                    <span id="latency1Value" class="test-details">-</span>
                                </div>
                            </div>
                        </div>
                        
                        <!-- 检测2:延迟变化趋势 -->
                        <div class="test-item" style="border-bottom: 1px solid #e2e8f0; padding: 12px 0;">
                            <div style="display: flex; justify-content: space-between; align-items: center;">
                                <div>
                                    <div style="font-weight: 600; color: #2d3748; font-size: 14px;">延迟变化趋势</div>
                                    <div style="font-size: 12px; color: #718096; margin-top: 4px;">通过多次测试比较延迟波动情况</div>
                                </div>
                                <div style="display: flex; align-items: center; gap: 12px;">
                                    <span id="latency2Status" class="test-status status-pending">待检测</span>
                                    <span id="latency2Value" class="test-details">-</span>
                                </div>
                            </div>
                        </div>
                        
                        <!-- 检测3:相对网络质量 -->
                        <div class="test-item" style="padding: 12px 0;">
                            <div style="display: flex; justify-content: space-between; align-items: center;">
                                <div>
                                    <div style="font-weight: 600; color: #2d3748; font-size: 14px;">相对网络质量</div>
                                    <div style="font-size: 12px; color: #718096; margin-top: 4px;">评估网络连接是稳定还是波动</div>
                                </div>
                                <div style="display: flex; align-items: center; gap: 12px;">
                                    <span id="latency3Status" class="test-status status-pending">待检测</span>
                                    <span id="latency3Value" class="test-details">-</span>
                                </div>
                            </div>
                        </div>
                        
                        <!-- 延迟曲线图 -->
                        <div style="margin-top: 24px;">
                            <h3 style="font-size: 14px; color: #2d3748; margin-bottom: 12px;">📈 10次测试延迟曲线图</h3>
                            <div style="background: #f7fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 8px; height: 300px;">
                                <canvas id="latencyChart"></canvas>
                            </div>
                        </div>
                        
                        <!-- 路由追踪说明 -->
                        <div style="margin-top: 20px;">
                            <h3 style="font-size: 14px; color: #2d3748; margin-bottom: 8px;">🔍 路由追踪说明(需手动执行)</h3>
                            <div style="background: #fff3cd; border: 1px solid #ffc107; border-radius: 8px; padding: 12px;">
                                <p style="color: #856404; font-size: 13px; line-height: 1.6; margin: 0;">
                                    <strong>Windows用户:</strong>打开命令提示符,输入 <code style="background: #f8f9fa; padding: 2px 6px; border-radius: 4px;">tracert super.boluobao.ai</code><br>
                                    <strong>Mac用户:</strong>打开终端,输入 <code style="background: #f8f9fa; padding: 2px 6px; border-radius: 4px;">traceroute super.boluobao.ai</code>
                                </p>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            
            <!-- 6. 安全与证书检测 -->
            <div class="section">
                <div class="section-header">
                    <h2 class="section-title">6. 安全与证书检测</h2>
                    <span id="securityStatus" class="section-status status-pending">待检测</span>
                </div>
                <div class="section-content">
                    <div style="margin-bottom: 16px;">
                        <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; padding: 24px; color: white;">
                            <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;">
                                <div style="width: 48px; height: 48px; background: rgba(255,255,255,0.2); border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 24px;">
                                    🔒
                                </div>
                                <div>
                                    <h3 style="font-size: 18px; font-weight: 600; margin: 0;">浏览器安全限制</h3>
                                    <p style="font-size: 13px; opacity: 0.9; margin: 4px 0 0 0;">前端无法直接读取证书信息</p>
                                </div>
                            </div>
                            <p style="font-size: 14px; line-height: 1.8; margin: 0 0 20px 0; opacity: 0.95;">
                                由于浏览器安全策略,前端 JavaScript 没有 API 可以直接读取 SSL 证书的详细信息(如证书名称、有效期、指纹等)。
                            </p>
                            <div style="background: rgba(255,255,255,0.15); border-radius: 8px; padding: 16px;">
                                <h4 style="font-size: 15px; font-weight: 600; margin: 0 0 12px 0; display: flex; align-items: center; gap: 8px;">
                                    <span>📋</span> 如何手动查看证书详情?
                                </h4>
                                <ol style="font-size: 14px; line-height: 2; margin: 0; padding-left: 20px;">
                                    <li>访问 <code style="background: rgba(0,0,0,0.2); padding: 2px 8px; border-radius: 4px; font-size: 13px;">https://super.boluobao.ai</code></li>
                                    <li>点击浏览器地址栏左侧的锁图标 🔒</li>
                                    <li>选择"证书"或"连接是安全的"</li>
                                    <li>查看"基本信息"或"详细信息"标签页</li>
                                    <li>确认"公用名 (CN)"是否包含 *.super.boluobao.ai</li>
                                </ol>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            

            
            <!-- 检测结果汇总 -->
            <div id="summarySection" class="summary" style="display: none;">
                <h3>检测结果汇总</h3>
                <div id="summaryContent"></div>
            </div>
            
            <!-- 故障排除建议 -->
            <div id="troubleshootingSection" class="troubleshooting" style="display: none;">
                <h3>故障排除建议</h3>
                <div id="troubleshootingContent"></div>
            </div>
            
            <!-- 详细日志 -->
            <div class="log-controls">
                <h3>详细日志</h3>
                <div class="log-buttons">
                    <button id="copyLogBtn" class="btn btn-secondary">
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
                            <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
                        </svg>
                        复制日志
                    </button>
                    <button id="exportLogBtn" class="btn btn-secondary">
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
                            <polyline points="7 10 12 15 17 10"></polyline>
                            <line x1="12" y1="15" x2="12" y2="3"></line>
                        </svg>
                        导出日志
                    </button>
                </div>
            </div>
            <div class="log-container" id="logContainer"></div>
        </div>
    </div>

    <script>
        const TARGET_URL = 'https://super.boluobao.ai';
        const TEST_TIMEOUT = 15000;
        let isRunning = false;
        let abortController = null;
        let testResults = {
            basic: false,
            site: false,
            resource: false,
            latency: false,
            security: false,
            commonWebsites: false
        };
        const logEntries = [];

        // DOM元素
        const elements = {
            startTestBtn: document.getElementById('startTestBtn'),
            stopTestBtn: document.getElementById('stopTestBtn'),
            clearLogBtn: document.getElementById('clearLogBtn'),
            copyLogBtn: document.getElementById('copyLogBtn'),
            exportLogBtn: document.getElementById('exportLogBtn'),
            progressFill: document.getElementById('progressFill'),
            logContainer: document.getElementById('logContainer'),
            summarySection: document.getElementById('summarySection'),
            summaryContent: document.getElementById('summaryContent'),
            troubleshootingSection: document.getElementById('troubleshootingSection'),
            troubleshootingContent: document.getElementById('troubleshootingContent'),
            latencyChart: null
        };

        // 日志函数
        function log(message, type = 'info') {
            const timestamp = new Date().toLocaleTimeString();
            logEntries.push({ timestamp, message, type });
            const logEntry = document.createElement('div');
            logEntry.className = `log-${type}`;
            logEntry.textContent = `[${timestamp}] ${message}`;
            elements.logContainer.appendChild(logEntry);
            elements.logContainer.scrollTop = elements.logContainer.scrollHeight;
        }
        
        // 复制日志函数
        function copyLog() {
            if (logEntries.length === 0) {
                alert('暂无日志可复制');
                return;
            }
            const logText = logEntries.map(entry => `[${entry.timestamp}] ${entry.message}`).join('\n');
            navigator.clipboard.writeText(logText).then(() => {
                log('✓ 日志已复制到剪贴板', 'success');
            }).catch(err => {
                console.error('复制失败:', err);
                alert('复制失败,请手动选择复制');
            });
        }
        
        // 导出日志函数
        function exportLog() {
            if (logEntries.length === 0) {
                alert('暂无日志可导出');
                return;
            }
            const logText = logEntries.map(entry => `[${entry.timestamp}] ${entry.message}`).join('\n');
            const blob = new Blob([logText], { type: 'text/plain;charset=utf-8' });
            const now = new Date();
            const dateStr = now.getFullYear() + 
                String(now.getMonth() + 1).padStart(2, '0') + 
                String(now.getDate()).padStart(2, '0') + '_' + 
                String(now.getHours()).padStart(2, '0') + 
                String(now.getMinutes()).padStart(2, '0') + 
                String(now.getSeconds()).padStart(2, '0');
            const filename = `boluobao_network_test_log_${dateStr}.txt`;
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
            log(`✓ 日志已导出为 ${filename}`, 'success');
        }

        // 更新进度条
        function updateProgress(percent) {
            elements.progressFill.style.width = `${percent}%`;
        }

        // 带超时的fetch请求
        async function fetchWithTimeout(url, options = {}) {
            const controller = new AbortController();
            const timeoutId = setTimeout(() => controller.abort(), options.timeout || TEST_TIMEOUT);
            
            try {
                const response = await fetch(url, {
                    ...options,
                    signal: controller.signal
                });
                clearTimeout(timeoutId);
                return response;
            } catch (error) {
                clearTimeout(timeoutId);
                throw error;
            }
        }

        // 常见网站数据
        const commonWebsites = [
            { name: 'boluobaoAI', url: 'https://super.boluobao.ai' },
            { name: 'Baidu', url: 'https://www.baidu.com' },
            { name: 'Google', url: 'https://www.google.com' },
            { name: 'Facebook', url: 'https://www.facebook.com' },
            { name: 'X', url: 'https://www.x.com' }
        ];

        // 1. 常见网站连通信测试
        async function runCommonWebsitesTests() {
            log('开始常见网站连通信测试...');
            document.getElementById('commonWebsitesStatus').className = 'section-status status-info';
            document.getElementById('commonWebsitesStatus').textContent = '检测中';
            
            let successCount = 0;
            let failCount = 0;
            
            for (const site of commonWebsites) {
                const elementId = `test-${site.name}`;
                const statusId = `test-${site.name}-status`;
                
                const element = document.getElementById(elementId);
                const statusElement = document.getElementById(statusId);
                
                if (!element || !statusElement) {
                    log(`  ⚠️ 找不到 ${site.name} 的 DOM 元素,跳过`, 'warning');
                    continue;
                }
                
                element.style.borderColor = '#e9ecef';
                element.style.background = '#fff3cd';
                statusElement.textContent = '检测中...';
                statusElement.style.color = '#856404';
                
                log(`测试 ${site.name} (${site.url})...`);
                
                const latency = await testRealSiteConnectivity(site.url);
                
                if (latency < 9999) {
                    element.style.borderColor = '#48bb78';
                    element.style.background = '#f0fff4';
                    statusElement.textContent = `✓ 连通 (${latency}ms)`;
                    statusElement.style.color = '#276749';
                    log(`  ✓ ${site.name} 连接成功 (${latency}ms)`, 'success');
                    successCount++;
                } else {
                    element.style.borderColor = '#fc8181';
                    element.style.background = '#fff5f5';
                    statusElement.textContent = '✗ 无法连接';
                    statusElement.style.color = '#c53030';
                    log(`  ✗ ${site.name} 连接失败`, 'error');
                    failCount++;
                }
                
                await new Promise(r => setTimeout(r, 300));
            }
            
            document.getElementById('commonWebsitesStatus').className = 'section-status status-success';
            document.getElementById('commonWebsitesStatus').textContent = '完成';
            testResults.commonWebsites = true;
            log(`✓ 常见网站连通信测试完成: ${successCount}/${commonWebsites.length} 个成功`, 'success');
        }

        // 解析HTML中的资源链接
        function parseResourcesFromHTML(html, baseUrl) {
            const resources = [];
            const base = new URL(baseUrl);
            
            // 匹配所有的资源链接
            const resourcePatterns = [
                // <link rel="stylesheet" href="...">
                { pattern: /<link[^>]*href=["']([^"']+)["'][^>]*>/gi, type: 'stylesheet', category: 'css' },
                // <script src="...">
                { pattern: /<script[^>]*src=["']([^"']+)["'][^>]*>/gi, type: 'script', category: 'js' },
                // <img src="...">
                { pattern: /<img[^>]*src=["']([^"']+)["'][^>]*>/gi, type: 'img', category: 'image' },
                // <source src="...">
                { pattern: /<source[^>]*src=["']([^"']+)["'][^>]*>/gi, type: 'source', category: 'other' },
            ];
            
            resourcePatterns.forEach(({ pattern, type, category }) => {
                let match;
                while ((match = pattern.exec(html)) !== null) {
                    let src = match[1];
                    if (src && !src.startsWith('data:') && !src.startsWith('javascript:')) {
                        try {
                            const fullUrl = new URL(src, base).href;
                            const name = fullUrl.split('/').pop() || src;
                            
                            // 根据扩展名更精确地确定类型
                            let finalType = type;
                            let finalCategory = category;
                            
                            if (name.endsWith('.css')) {
                                finalType = 'stylesheet';
                                finalCategory = 'css';
                            } else if (name.endsWith('.js')) {
                                finalType = 'script';
                                finalCategory = 'js';
                            } else if (/\.(png|jpg|jpeg|gif|svg|webp|ico)$/i.test(name)) {
                                finalType = name.split('.').pop();
                                finalCategory = 'image';
                            } else if (/\.(woff|woff2|ttf|otf|eot)$/i.test(name)) {
                                finalType = 'font';
                                finalCategory = 'font';
                            }
                            
                            // 避免重复
                            if (!resources.some(r => r.url === fullUrl)) {
                                resources.push({ 
                                    url: fullUrl, 
                                    name: name.length > 40 ? name.substring(0, 37) + '...' : name, 
                                    type: finalType, 
                                    category: finalCategory 
                                });
                            }
                        } catch (e) {
                            // 忽略无效URL
                        }
                    }
                }
            });
            
            return resources;
        }



        // 2. 基础网络信息检测(已修正+完整中文IP信息)
        async function runBasicNetworkTests() {
            log('开始基础网络信息检测...');
            document.getElementById('basicStatus').className = 'section-status status-info';
            document.getElementById('basicStatus').textContent = '检测中';
            
            let allPassed = true;
            
            // 1.1 浏览器访问状态
            try {
                const isOnline = navigator.onLine;
                if (isOnline) {
                    document.getElementById('browserStatus').textContent = '已连接';
                    document.getElementById('browserStatus').style.color = '#22543d';
                    log('✓ 浏览器网络状态正常', 'success');
                } else {
                    document.getElementById('browserStatus').textContent = '离线';
                    document.getElementById('browserStatus').style.color = '#742a2a';
                    log('✗ 浏览器处于离线状态', 'error');
                    allPassed = false;
                }
            } catch (error) {
                document.getElementById('browserStatus').textContent = '未知';
                log(`⚠ 浏览器网络状态检测失败: ${error.message}`, 'warning');
            }
            
            updateProgress(10);
            
            // 1.2 系统时区和时间
            try {
                const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
                document.getElementById('systemTimezone').textContent = timezone;
                log(`✓ 系统时区: ${timezone}`, 'success');
                
                const updateTime = () => {
                    const now = new Date();
                    document.getElementById('systemTime').textContent = now.toLocaleString();
                };
                updateTime();
                setInterval(updateTime, 1000);
            } catch (error) {
                document.getElementById('systemTimezone').textContent = '检测失败';
                log(`⚠ 系统时间检测失败: ${error.message}`, 'warning');
            }
            
            updateProgress(20);
            
            // 1.3 网络类型
            try {
                // 尝试多种浏览器兼容性的方式获取网络信息
                let networkInfo = '';
                let hasInfo = false;
                let connection = null;
                
                // 1. 标准方式 (Chrome/Edge)
                if (navigator.connection) {
                    connection = navigator.connection;
                }
                // 2. Firefox 浏览器
                else if (navigator.mozConnection) {
                    connection = navigator.mozConnection;
                }
                // 3. Safari/WebKit 浏览器
                else if (navigator.webkitConnection) {
                    connection = navigator.webkitConnection;
                }
                
                if (connection) {
                    let typeInfo = '';
                    let speedInfo = '';
                    
                    if (connection.effectiveType) {
                        typeInfo = connection.effectiveType.toUpperCase();
                    } else if (connection.type) {
                        typeInfo = connection.type.toUpperCase();
                    }
                    
                    if (connection.downlink) {
                        speedInfo = `${connection.downlink} Mbps`;
                    } else if (connection.downlinkMax) {
                        speedInfo = `≤${connection.downlinkMax} Mbps`;
                    }
                    
                    if (typeInfo && speedInfo) {
                        networkInfo = `${typeInfo} (${speedInfo})`;
                        hasInfo = true;
                    } else if (typeInfo) {
                        networkInfo = typeInfo;
                        hasInfo = true;
                    } else if (speedInfo) {
                        networkInfo = speedInfo;
                        hasInfo = true;
                    }
                }
                
                // 回退方案:如果没有网络类型API不可用,至少显示在线状态
                if (!hasInfo) {
                    if (navigator.onLine) {
                        networkInfo = '在线';
                        hasInfo = true;
                    } else {
                        networkInfo = '离线';
                        hasInfo = true;
                    }
                }
                
                if (hasInfo) {
                    document.getElementById('networkType').textContent = networkInfo;
                    log(`✓ 网络类型: ${networkInfo}`, 'success');
                } else {
                    document.getElementById('networkType').textContent = '不支持';
                    log('⚠ 浏览器不支持网络信息API', 'warning');
                }
            } catch (error) {
                // 最坏情况:至少显示在线状态
                if (navigator.onLine) {
                    document.getElementById('networkType').textContent = '在线';
                    log('✓ 网络类型: 在线', 'success');
                } else {
                    document.getElementById('networkType').textContent = '离线';
                    log('✓ 网络类型: 离线', 'success');
                }
            }
            
            updateProgress(30);
            
            // 1.4 出口IP + 完整地理位置信息(中文展示)
            let ipDetected = false;
            const ipServices = [
                { url: 'https://freeipapi.com/api/json', timeout: 20000 },
                { url: 'https://ipinfo.io/json', timeout: 20000 },
                { url: 'https://api.my-ip.io/v2/ip.json', timeout: 20000 }
            ];
            
            for (const service of ipServices) {
                if (ipDetected) break;
                try {
                    log(`尝试使用 ${service.url} 查询IP信息...`, 'info');
                    const response = await fetchWithTimeout(service.url, { timeout: service.timeout });
                    
                    if (!response.ok) {
                        throw new Error(`HTTP ${response.status}`);
                    }
                    
                    const data = await response.json();
                    
                    let ip, org, city, region, country, loc, postal, timezone;
                    
                    if (service.url.includes('ip-api.com')) {
                        ip = data.query;
                        org = data.isp;
                        city = data.city;
                        region = data.regionName;
                        country = data.country;
                        loc = data.lat && data.lon ? `${data.lat},${data.lon}` : null;
                        postal = data.zip;
                        timezone = data.timezone;
                    } else if (service.url.includes('ipapi.co')) {
                        ip = data.ip;
                        org = data.org;
                        city = data.city;
                        region = data.region;
                        country = data.country;
                        loc = data.latitude && data.longitude ? `${data.latitude},${data.longitude}` : null;
                        postal = data.postal;
                        timezone = data.timezone;
                    } else if (service.url.includes('ip.sb')) {
                        ip = data.ip;
                        org = data.org || data.isp;
                        city = data.city;
                        region = data.region;
                        country = data.country;
                        loc = data.latitude && data.longitude ? `${data.latitude},${data.longitude}` : null;
                        postal = data.postal;
                        timezone = data.timezone;
                    } else if (service.url.includes('my-ip.io')) {
                        ip = data.ip;
                        org = data.isp;
                        city = data.city;
                        region = data.region;
                        country = data.country?.code || data.country;
                        loc = data.location?.lat && data.location?.lon ? `${data.location.lat},${data.location.lon}` : null;
                        postal = data.asn?.number || data.zipCode;
                        timezone = data.timeZone || data.timezone;
                    } else if (service.url.includes('freeipapi.com')) {
                        ip = data.ipAddress;
                        org = data.isp;
                        city = data.city;
                        region = data.region;
                        country = data.countryName;
                        loc = data.latitude && data.longitude ? `${data.latitude},${data.longitude}` : null;
                        postal = data.zipCode;
                        timezone = data.timeZone;
                    } else {
                        ip = data.ip;
                        org = data.org;
                        city = data.city;
                        region = data.region;
                        country = data.country;
                        loc = data.loc;
                        postal = data.postal;
                        timezone = data.timezone;
                    }
                    
                    if (ip) {
                        // 核心IP信息
                        document.getElementById('exitIp').textContent = ip || '未知';
                        document.getElementById('isp').textContent = org || '未知运营商';
                        // 完整扩展信息
                        document.getElementById('ipCity').textContent = city || '未知城市';
                        document.getElementById('ipRegion').textContent = region || '未知地区';
                        document.getElementById('ipCountry').textContent = country || '未知国家';
                        document.getElementById('ipLoc').textContent = loc || '未知经纬度';
                        document.getElementById('ipPostal').textContent = postal || '无邮编';
                        document.getElementById('ipTimezone').textContent = timezone || '未知时区';
                        
                        log(`✓ 出口IP: ${ip},运营商: ${org || '未知'}`, 'success');
                        log(`✓ IP地理位置: ${country}-${region}-${city}`, 'success');
                        ipDetected = true;
                        break;
                    }
                } catch (error) {
                    log(`⚠ ${service.url} 查询失败: ${error.message}`, 'warning');
                    continue;
                }
            }
            
            if (!ipDetected) {
                // 所有IP信息赋值失败,但不影响其他检测
                document.getElementById('exitIp').textContent = '未知';
                document.getElementById('isp').textContent = '未知';
                document.getElementById('ipCity').textContent = '未知';
                document.getElementById('ipRegion').textContent = '未知';
                document.getElementById('ipCountry').textContent = '未知';
                document.getElementById('ipLoc').textContent = '未知';
                document.getElementById('ipPostal').textContent = '未知';
                document.getElementById('ipTimezone').textContent = '未知';
                
                log('⚠ 所有IP查询服务暂时不可用,但这不影响其他检测', 'warning');
                // 不设置 allPassed = false,让检测继续进行
            }
            
            updateProgress(50);
            
            document.getElementById('basicStatus').className = 'section-status status-success';
            document.getElementById('basicStatus').textContent = '完成';
            log('✓ 基础网络信息检测完成', 'success');
            
            testResults.basic = allPassed;
            return allPassed;
        }

        // 3. 站点连通性检测
        async function runSiteConnectivityTests() {
            log('开始站点连通性检测...');
            document.getElementById('siteStatus').className = 'section-status status-info';
            document.getElementById('siteStatus').textContent = '检测中';
            
            let allPassed = true;
            
            // 3.1 站点IP地址 - 使用更兼容的检测方式
            try {
                let ips = '';
                let success = false;
                
                // 方法1: 使用Google DNS
                try {
                    const dnsResponse = await fetchWithTimeout('https://dns.google/resolve?name=super.boluobao.ai&type=A', {
                        timeout: 8000
                    });
                    const dnsData = await dnsResponse.json();
                    
                    if (dnsData.Answer && dnsData.Answer.length > 0) {
                        ips = dnsData.Answer.map(a => a.data).join(', ');
                        success = true;
                    }
                } catch (e) {
                    // 方法1失败,尝试其他方式
                }
                
                // 如果方法1失败,尝试方法2
                if (!success) {
                    try {
                        const dnsResponse = await fetchWithTimeout('https://1.1.1.1/dns-query?name=super.boluobao.ai&type=A', {
                            headers: { 'Accept': 'application/dns-json' },
                            timeout: 8000
                        });
                        const dnsData = await dnsResponse.json();
                        
                        if (dnsData.Answer && dnsData.Answer.length > 0) {
                            ips = dnsData.Answer.map(a => a.data).join(', ');
                            success = true;
                        }
                    } catch (e2) {
                        // 方法2也失败
                    }
                }
                
                if (success) {
                    document.getElementById('siteIpResult').className = 'test-status status-success';
                    document.getElementById('siteIpResult').textContent = '成功';
                    document.getElementById('siteIpDetails').textContent = ips;
                    log(`✓ 站点IP地址: ${ips}`, 'success');
                } else {
                    document.getElementById('siteIpResult').className = 'test-status status-warning';
                    document.getElementById('siteIpResult').textContent = '检测中';
                    document.getElementById('siteIpDetails').textContent = '浏览器无法直接获取DNS';
                    log('⚠ 浏览器无法直接获取DNS记录,请访问站点查看连通性', 'warning');
                }
            } catch (error) {
                document.getElementById('siteIpResult').className = 'test-status status-warning';
                document.getElementById('siteIpResult').textContent = '检测中';
                document.getElementById('siteIpDetails').textContent = '浏览器无法直接获取DNS';
                log('⚠ 浏览器无法直接获取DNS记录', 'warning');
            }
            
            updateProgress(60);
            
            // 2.2 HTTP 80端口连通性 - 使用图片检测法避免CORS问题
            try {
                const startTime = performance.now();
                const img = new Image();
                
                const imgPromise = new Promise((resolve, reject) => {
                    img.onload = () => resolve(true);
                    img.onerror = () => reject(new Error('Connection failed'));
                    img.src = 'http://super.boluobao.ai/favicon.ico?' + Date.now();
                });
                
                const timeoutPromise = new Promise((_, reject) => 
                    setTimeout(() => reject(new Error('Timeout')), 10000)
                );
                
                await Promise.race([imgPromise, timeoutPromise]);
                const endTime = performance.now();
                const duration = Math.round(endTime - startTime);
                
                document.getElementById('http80Result').className = 'test-status status-success';
                document.getElementById('http80Result').textContent = '成功';
                document.getElementById('http80Details').textContent = `${duration}ms`;
                log(`✓ HTTP 80端口连通性正常,响应时间: ${duration}ms`, 'success');
            } catch (error) {
                document.getElementById('http80Result').className = 'test-status status-failed';
                document.getElementById('http80Result').textContent = '失败';
                document.getElementById('http80Details').textContent = error.message;
                log(`✗ HTTP 80端口连接失败: ${error.message}`, 'error');
                allPassed = false;
            }
            
            updateProgress(70);
            
            // 2.3 HTTPS 443端口连通性 - 使用图片检测法避免CORS问题
            try {
                const startTime = performance.now();
                // 使用图片加载方式检测连通性,避免CORS问题
                const img = new Image();
                
                const imgPromise = new Promise((resolve, reject) => {
                    img.onload = () => resolve(true);
                    img.onerror = () => reject(new Error('Connection failed'));
                    img.src = TARGET_URL + ':443' + '/favicon.ico?' + Date.now(); // 添加时间戳避免缓存
                });
                
                const timeoutPromise = new Promise((_, reject) => 
                    setTimeout(() => reject(new Error('Timeout')), 10000)
                );
                
                await Promise.race([imgPromise, timeoutPromise]);
                const endTime = performance.now();
                const duration = Math.round(endTime - startTime);
                
                document.getElementById('https443Result').className = 'test-status status-success';
                document.getElementById('https443Result').textContent = '成功';
                document.getElementById('https443Details').textContent = `${duration}ms`;
                log(`✓ HTTPS 443端口连通性正常,响应时间: ${duration}ms`, 'success');
            } catch (error) {
                document.getElementById('https443Result').className = 'test-status status-failed';
                document.getElementById('https443Result').textContent = '失败';
                document.getElementById('https443Details').textContent = error.message;
                log(`✗ HTTPS 443端口连接失败: ${error.message}`, 'error');
                allPassed = false;
            }
            
            updateProgress(80);
            
            document.getElementById('siteStatus').className = 'section-status status-success';
            document.getElementById('siteStatus').textContent = '完成';
            log('✓ 站点连通性检测完成', 'success');
            
            testResults.site = allPassed;
            return allPassed;
        }

        // 4. 资源连通性检测 - 使用指定链接列表
        async function runResourceTests() {
            log('开始资源连通性检测,将持续30秒...');
            document.getElementById('resourceStatus').className = 'section-status status-info';
            document.getElementById('resourceStatus').textContent = '检测中 (0/30s)';
            
            const tableBody = document.getElementById('networkTableBody');
            tableBody.innerHTML = '<tr><td colspan="6" style="text-align: center; color: #4299e1; padding: 20px;">正在检测资源...</td></tr>';
            
            document.getElementById('networkStats').style.display = 'none';
            
            // 清理之前的performance entries
            if (performance.clearResourceTimings) {
                performance.clearResourceTimings();
            }
            
            const requests = new Map();
            let totalTime = 0;
            let detectStartTime = performance.now();
            
            // 使用用户指定的链接列表
            const resourcesToTest = [
                { url: 'https://super.boluobao.ai/', name: '首页', type: 'document', category: 'html', useImage: true },
                { url: 'https://super.boluobao.ai/zh', name: '中文首页', type: 'document', category: 'html', useImage: true },
                { url: 'https://super.boluobao.ai/zh/home', name: '中文主页', type: 'document', category: 'html', useImage: true },
                { url: 'https://super.boluobao.ai/assets/logo-s.png', name: 'logo-s.png', type: 'png', category: 'image', useImage: true },
                { url: 'https://super.boluobao.ai/api/canva/agent-cashier/task/query/unlimited', name: 'API: task/query', type: 'api', category: 'other' },
                { url: 'https://super.boluobao.ai/api/www/landing-activities/getById?id=46', name: 'API: getById=46', type: 'api', category: 'other' },
                { url: 'https://super.boluobao.ai/api/www/landing-activities/getById?id=52', name: 'API: getById=52', type: 'api', category: 'other' },
                { url: 'https://super.boluobao.ai/api/canva/agent-cashier/pricing/timeVariantConfig', name: 'API: pricing/config', type: 'api', category: 'other' },
                { url: 'https://super.boluobao.ai/api/www/boluobao/member/free/power', name: 'API: member/power', type: 'api', category: 'other' },
                { url: 'https://super.boluobao.ai/api/www/boluobao/member/account', name: 'API: member/account', type: 'api', category: 'other' },
                { url: 'https://super.boluobao.ai/api/www/boluobao/member/packages?packageType=TRAIN&libId=0&language=zh', name: 'API: member/packages', type: 'api', category: 'other' },
            ];
            
            return new Promise((resolve) => {
                // 更新状态显示
                let elapsed = 0;
                const statusInterval = setInterval(() => {
                    elapsed += 1;
                    document.getElementById('resourceStatus').textContent = `检测中 (${elapsed}/30s)`;
                }, 1000);
                
                // 清空之前的 performance 记录
                if (performance.clearResourceTimings) {
                    performance.clearResourceTimings();
                }
                
                // 记录我们要请求的 URL
                const expectedUrls = new Set(resourcesToTest.map(r => r.url));
                
                // 开始请求所有资源
                log(`开始请求 ${resourcesToTest.length} 个资源...`, 'info');
                resourcesToTest.forEach((resource, index) => {
                    log(`  [${index + 1}] ${resource.name}: ${resource.url}`, 'info');
                });
                
                resourcesToTest.forEach((resource, index) => {
                    requestResource(resource, index * 300); // 300ms间隔
                });
                
                // 完全禁用 performance API 收集,避免缓存干扰
                const checkInterval = null;
                
                // 30秒后结束
                setTimeout(() => {
                    finishCollection(checkInterval);
                }, 30000);
                
                // 添加手动请求的资源
                async function requestResource(resource, delay = 0) {
                    await new Promise(r => setTimeout(r, delay));
                    
                    const startTime = performance.now() - detectStartTime;
                    
                    try {
                        const requestStartTime = performance.now();
                        
                        // 使用 Image 方式检测(适用于所有资源,避免 CORS 问题)
                        await new Promise((resolve, reject) => {
                            const img = new Image();
                            const timeout = setTimeout(() => reject(new Error('Timeout')), 10000);
                            let done = false;
                            
                            const handleResult = (isSuccess) => {
                                if (done) return;
                                done = true;
                                clearTimeout(timeout);
                                const latency = performance.now() - requestStartTime;
                                
                                if (latency < 100 || !navigator.onLine) {
                                    reject(new Error('Cache or offline'));
                                } else if (isSuccess) {
                                    resolve();
                                } else {
                                    // 我们自己的网站资源,即使是error只要有延迟也可能成功
                                    if (latency >= 200) {
                                        resolve();
                                    } else {
                                        reject(new Error('Too fast'));
                                    }
                                }
                            };
                            
                            img.onload = () => handleResult(true);
                            img.onerror = () => handleResult(false);
                            
                            const random = Math.random().toString(36).substring(2, 15);
                            img.src = resource.url + '?no_cache=' + Date.now() + '&r=' + random;
                        });
                        
                        const endTime = performance.now();
                        const duration = endTime - requestStartTime;
                        
                        // 直接添加到列表中(成功)
                        const url = resource.url;
                        if (!requests.has(url)) {
                            let name = url.split('/').pop() || url;
                            if (name.length > 40) {
                                name = name.substring(0, 37) + '...';
                            }
                            
                            const req = {
                                name: name,
                                fullUrl: url,
                                type: resource.type,
                                initiator: 'direct',
                                category: resource.category,
                                startTime: startTime,
                                duration: duration,
                                statusText: '成功',
                                statusClass: 'status-ok',
                                fromCache: false
                            };
                            
                            requests.set(url, req);
                            totalTime = Math.max(totalTime, startTime + duration);
                            updateTable();
                        }
                        
                        log(`✓ 成功加载: ${resource.name} (${Math.round(duration)}ms)`, 'success');
                        
                    } catch (error) {
                        // 添加失败的资源
                        const url = resource.url;
                        if (!requests.has(url)) {
                            let name = url.split('/').pop() || url;
                            if (name.length > 40) {
                                name = name.substring(0, 37) + '...';
                            }
                            
                            const req = {
                                name: name,
                                fullUrl: url,
                                type: resource.type,
                                initiator: 'direct',
                                category: resource.category,
                                startTime: startTime,
                                duration: 10000,
                                statusText: '失败',
                                statusClass: 'status-error',
                                fromCache: false
                            };
                            
                            requests.set(url, req);
                            totalTime = Math.max(totalTime, startTime + 10000);
                            updateTable();
                        }
                        
                        log(`✗ 加载失败: ${resource.name} - ${error.message}`, 'error');
                    }
                }
                
                function addResourceFromEntry(entry) {
                    const url = entry.name;
                    
                    if (requests.has(url)) {
                        return;
                    }
                    
                    let name = url.split('/').pop() || url;
                    if (name.length > 40) {
                        name = name.substring(0, 37) + '...';
                    }
                    if (!name || name === '') {
                        name = url;
                    }
                    
                    // 判断资源类型
                    let type = 'other';
                    let category = 'other';
                    
                    if (entry.initiatorType) {
                        type = entry.initiatorType;
                        if (type === 'link') type = 'css';
                        if (type === 'script') category = 'js';
                        if (type === 'css' || type === 'link') category = 'css';
                        if (type === 'img' || type === 'image') category = 'image';
                    }
                    
                    if (url.endsWith('.css')) { category = 'css'; type = 'stylesheet'; }
                    if (url.endsWith('.js')) { category = 'js'; type = 'script'; }
                    if (/\.(png|jpg|jpeg|gif|svg|webp)$/i.test(url)) { 
                        category = 'image'; 
                        type = url.split('.').pop();
                    }
                    if (/\.(woff|woff2|ttf|otf|eot)$/i.test(url)) {
                        category = 'font';
                        type = 'font';
                    }
                    if (url === TARGET_URL || url === TARGET_URL + '/') {
                        category = 'html';
                        type = 'document';
                    }
                    
                    const duration = Math.round(entry.duration);
                    const startTimeEntry = Math.round(entry.startTime);
                    
                    const req = {
                        name: name,
                        fullUrl: url,
                        type: type,
                        initiator: entry.initiatorType || 'fetch',
                        category: category,
                        startTime: startTimeEntry,
                        duration: duration,
                        statusText: '200',
                        statusClass: 'status-ok',
                        fromCache: entry.transferSize === 0 && (entry.encodedBodySize > 0 || entry.decodedBodySize > 0)
                    };
                    
                    requests.set(url, req);
                    totalTime = Math.max(totalTime, startTimeEntry + duration);
                    
                    updateTable();
                }
                
                // 更新表格显示
                function updateTable() {
                    const tableBody = document.getElementById('networkTableBody');
                    tableBody.innerHTML = '';
                    
                    const requestArray = Array.from(requests.values());
                    
                    for (const req of requestArray) {
                        // 瀑布图位置和宽度
                        const waterfallLeft = (req.startTime / 30000) * 100;
                        const waterfallWidth = Math.max((req.duration / 30000) * 100, 0.5);
                        
                        // 时间格式化
                        const timeText = req.duration > 0 ? Math.round(req.duration) + ' ms' : '-';
                        
                        const row = document.createElement('tr');
                        row.innerHTML = `
                            <td class="col-name" title="${req.fullUrl}">${req.name}</td>
                            <td class="col-status ${req.statusClass}">${req.statusText}</td>
                            <td class="col-type">${req.type}</td>
                            <td class="col-initiator">${req.initiator}</td>
                            <td class="col-time">${timeText}</td>
                            <td class="col-waterfall">
                                <div class="waterfall-cell">
                                    <div class="waterfall-bar ${req.category}" 
                                         style="left: ${waterfallLeft}%; width: ${waterfallWidth}%;">
                                    </div>
                                </div>
                            </td>
                        `;
                        
                        tableBody.appendChild(row);
                    }
                    
                    // 更新统计
                    document.getElementById('networkStats').style.display = 'flex';
                    document.getElementById('requestCount').textContent = requestArray.length + ' 个请求';
                    document.getElementById('loadTime').textContent = '完成时间 ' + Math.round(totalTime) + ' ms';
                }
                
                // 结束收集
                function finishCollection(checkInterval) {
                    clearInterval(statusInterval);
                    if (checkInterval) {
                        clearInterval(checkInterval);
                    }
                    
                    updateProgress(95);
                    
                    document.getElementById('resourceStatus').className = 'section-status status-success';
                    document.getElementById('resourceStatus').textContent = '完成';
                    
                    if (requests.size > 0) {
                        log(`✓ 资源连通性检测完成,共收集 ${requests.size} 个资源`, 'success');
                        
                        // 输出所有资源信息到日志
                        log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'info');
                        log('资源详情列表:', 'info');
                        log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'info');
                        
                        const requestArray = Array.from(requests.values());
                        requestArray.forEach((req, index) => {
                            const logMsg = `[${index + 1}] 名称: ${req.name} | 类型: ${req.type} | 启动器: ${req.initiator} | 时间: ${req.duration}ms | URL: ${req.fullUrl}`;
                            log(logMsg, 'info');
                        });
                        
                        log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'info');
                    } else {
                        log('资源连通性检测完成,但未收集到有效资源(可能是跨域限制)', 'warning');
                    }
                    
                    testResults.resource = true;
                    resolve(true);
                }
            });
        }

        // 5. 网络抖动检测
        async function runLatencyTests() {
            log('开始网络性能检测...');
            document.getElementById('latencyStatus').className = 'section-status status-info';
            document.getElementById('latencyStatus').textContent = '检测中';
            
            // 设置初始状态
            document.getElementById('latency1Status').className = 'test-status status-info';
            document.getElementById('latency1Status').textContent = '检测中';
            document.getElementById('latency2Status').className = 'test-status status-info';
            document.getElementById('latency2Status').textContent = '检测中';
            document.getElementById('latency3Status').className = 'test-status status-info';
            document.getElementById('latency3Status').textContent = '检测中';
            
            const testCount = 10;
            const latencyResults = [];
            
            // 使用Image对象测试延迟(避免CORS问题)
            for (let i = 0; i < testCount; i++) {
                const testUrl = TARGET_URL + '/favicon.ico?t=' + Date.now() + '&i=' + i;
                const latency = await testSingleLatency(testUrl);
                latencyResults.push(latency);
                if (latency < 9999) {
                    log(`  第${i + 1}次测试: ${latency}ms`, 'info');
                } else {
                    log(`  第${i + 1}次测试: 失败`, 'warning');
                }
                await new Promise(r => setTimeout(r, 300));
            }
            
            // 计算统计数据
            const validLatencies = latencyResults.filter(l => l < 9999);
            if (validLatencies.length > 0) {
                const avgLatency = Math.round(validLatencies.reduce((a, b) => a + b, 0) / validLatencies.length);
                const minLatency = Math.min(...validLatencies);
                const maxLatency = Math.max(...validLatencies);
                
                // 计算延迟变化
                let jitterSum = 0;
                for (let i = 1; i < validLatencies.length; i++) {
                    jitterSum += Math.abs(validLatencies[i] - validLatencies[i - 1]);
                }
                const avgJitter = validLatencies.length > 1 ? Math.round(jitterSum / (validLatencies.length - 1)) : 0;
                
                // 检测1:服务器响应延迟
                document.getElementById('latency1Status').className = 'test-status status-success';
                document.getElementById('latency1Status').textContent = '正常';
                document.getElementById('latency1Value').textContent = `平均${avgLatency}ms (${minLatency}ms - ${maxLatency}ms)`;
                
                // 检测2:延迟变化趋势
                let trendText = '';
                let trendStatus = '';
                if (avgJitter < 50) {
                    trendText = '稳定';
                    trendStatus = 'status-success';
                } else if (avgJitter < 100) {
                    trendText = '轻微波动';
                    trendStatus = 'status-warning';
                } else {
                    trendText = '波动较大';
                    trendStatus = 'status-error';
                }
                document.getElementById('latency2Status').className = 'test-status ' + trendStatus;
                document.getElementById('latency2Status').textContent = trendText;
                document.getElementById('latency2Value').textContent = `平均变化${avgJitter}ms`;
                
                // 检测3:相对网络质量
                let qualityText = '';
                let qualityStatus = '';
                if (avgLatency < 100 && avgJitter < 50) {
                    qualityText = '优秀';
                    qualityStatus = 'status-success';
                } else if (avgLatency < 300 && avgJitter < 100) {
                    qualityText = '良好';
                    qualityStatus = 'status-success';
                } else if (avgLatency < 500 && avgJitter < 200) {
                    qualityText = '一般';
                    qualityStatus = 'status-warning';
                } else {
                    qualityText = '较差';
                    qualityStatus = 'status-error';
                }
                document.getElementById('latency3Status').className = 'test-status ' + qualityStatus;
                document.getElementById('latency3Status').textContent = qualityText;
                document.getElementById('latency3Value').textContent = `延迟${avgLatency}ms / 波动${avgJitter}ms`;
                
                // 绘制曲线图
                drawLatencyChart(latencyResults);
                
                log(`✓ 网络性能检测完成: 平均${avgLatency}ms, 变化${avgJitter}ms, 质量${qualityText}`, 'success');
            } else {
                document.getElementById('latency1Status').className = 'test-status status-error';
                document.getElementById('latency1Status').textContent = '失败';
                document.getElementById('latency1Value').textContent = '测试失败';
                
                document.getElementById('latency2Status').className = 'test-status status-error';
                document.getElementById('latency2Status').textContent = '失败';
                document.getElementById('latency2Value').textContent = '测试失败';
                
                document.getElementById('latency3Status').className = 'test-status status-error';
                document.getElementById('latency3Status').textContent = '失败';
                document.getElementById('latency3Value').textContent = '测试失败';
                
                log('网络性能检测失败', 'error');
            }
            
            document.getElementById('latencyStatus').className = 'section-status status-success';
            document.getElementById('latencyStatus').textContent = '完成';
            testResults.latency = true;
        }

        // 单个延迟测试(用于网络检测)
        function testSingleLatency(url) {
            return new Promise((resolve) => {
                if (!navigator.onLine) {
                    resolve(9999);
                    return;
                }
                
                const img = new Image();
                const startTime = performance.now();
                let done = false;
                
                const timeoutId = setTimeout(() => {
                    if (!done) resolve(9999);
                }, 8000);
                
                const handleResult = (isSuccess) => {
                    if (done) return;
                    done = true;
                    clearTimeout(timeoutId);
                    
                    const latency = Math.round(performance.now() - startTime);
                    
                    // 只有真正成功加载才算连通
                    if (isSuccess) {
                        resolve(latency);
                    } else {
                        resolve(9999);
                    }
                };
                
                img.onload = () => handleResult(true);
                img.onerror = () => handleResult(false);
                
                const random = Math.random().toString(36).substring(2, 15);
                const cacheBuster = '?no_cache=' + Date.now() + '&r=' + random;
                img.src = url.includes('?') ? url + '&no_cache=' + Date.now() + '&r=' + random : url + cacheBuster;
            });
        }
        
        // 网站连通性测试 - 平衡版
        function testRealSiteConnectivity(url) {
            return new Promise((resolve) => {
                if (!navigator.onLine) {
                    resolve(9999);
                    return;
                }
                
                const img = new Image();
                const startTime = performance.now();
                let done = false;
                
                const timeoutId = setTimeout(() => {
                    if (!done) resolve(9999);
                }, 10000);
                
                const handleResult = (isSuccess) => {
                    if (done) return;
                    done = true;
                    clearTimeout(timeoutId);
                    
                    const latency = Math.round(performance.now() - startTime);
                    
                    // 只有真正成功加载才算连通
                    if (isSuccess) {
                        resolve(latency);
                    } else {
                        resolve(9999);
                    }
                };
                
                img.onload = () => handleResult(true);
                img.onerror = () => handleResult(false);
                
                const random = Math.random().toString(36).substring(2, 12);
                let finalUrl;
                
				finalUrl = url.replace(/\/$/, '') + '/favicon.ico?no_cache=' + Date.now() + '&r=' + random;
                
                img.src = finalUrl;
            });
        }

        // 绘制延迟曲线图
        function drawLatencyChart(latencyData) {
            const canvas = document.getElementById('latencyChart');
            const ctx = canvas.getContext('2d');
            
            // 设置画布尺寸
            const container = canvas.parentElement;
            canvas.width = container.clientWidth - 16;
            canvas.height = container.clientHeight - 16;
            
            const padding = 30;
            const chartWidth = canvas.width - padding * 2;
            const chartHeight = canvas.height - padding * 2;
            
            // 清除画布
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // 绘制背景网格
            ctx.strokeStyle = '#e2e8f0';
            ctx.lineWidth = 1;
            
            // 横线
            for (let i = 0; i <= 4; i++) {
                const y = padding + (chartHeight / 4) * i;
                ctx.beginPath();
                ctx.moveTo(padding, y);
                ctx.lineTo(canvas.width - padding, y);
                ctx.stroke();
            }
            
            // 找到最大值,处理失败值
            const validData = latencyData.filter(l => l < 9999);
            const maxLatency = validData.length > 0 ? Math.max(...validData) * 1.2 : 500;
            
            // 绘制曲线
            ctx.strokeStyle = '#667eea';
            ctx.lineWidth = 3;
            ctx.beginPath();
            
            const stepX = chartWidth / (latencyData.length - 1);
            
            for (let i = 0; i < latencyData.length; i++) {
                const x = padding + i * stepX;
                const y = padding + chartHeight - (latencyData[i] / maxLatency) * chartHeight;
                
                if (latencyData[i] >= 9999) {
                    // 失败值画红色点
                    ctx.fillStyle = '#e53e3e';
                    ctx.beginPath();
                    ctx.arc(x, padding + chartHeight - 15, 6, 0, Math.PI * 2);
                    ctx.fill();
                    continue;
                }
                
                if (i === 0) {
                    ctx.moveTo(x, y);
                } else {
                    ctx.lineTo(x, y);
                }
            }
            ctx.stroke();
            
            // 绘制数据点
            for (let i = 0; i < latencyData.length; i++) {
                if (latencyData[i] >= 9999) continue;
                
                const x = padding + i * stepX;
                const y = padding + chartHeight - (latencyData[i] / maxLatency) * chartHeight;
                
                ctx.fillStyle = '#667eea';
                ctx.beginPath();
                ctx.arc(x, y, 6, 0, Math.PI * 2);
                ctx.fill();
                
                // 绘制数值
                ctx.fillStyle = '#4a5568';
                ctx.font = '14px sans-serif';
                ctx.textAlign = 'center';
                ctx.fillText(`${latencyData[i]}ms`, x, y - 12);
            }
            
            // 绘制坐标轴
            ctx.strokeStyle = '#718096';
            ctx.lineWidth = 1;
            ctx.beginPath();
            ctx.moveTo(padding, padding);
            ctx.lineTo(padding, canvas.height - padding);
            ctx.lineTo(canvas.width - padding, canvas.height - padding);
            ctx.stroke();
            
            // 绘制标签
            ctx.fillStyle = '#718096';
            ctx.font = '14px sans-serif';
            ctx.textAlign = 'center';
            for (let i = 0; i < latencyData.length; i++) {
                const x = padding + i * stepX;
                ctx.fillText(`#${i + 1}`, x, canvas.height - padding + 22);
            }
            
            ctx.textAlign = 'right';
            ctx.fillText(`0ms`, padding - 5, canvas.height - padding + 5);
            ctx.fillText(`${Math.round(maxLatency)}ms`, padding - 5, padding + 5);
        }

        // 6. 安全与证书检测
        async function runSecurityTests() {
            log('开始安全与证书检测...');
            document.getElementById('securityStatus').className = 'section-status status-success';
            document.getElementById('securityStatus').textContent = '完成';
            testResults.security = true;
            log('✓ 安全与证书检测完成,请查看说明手动验证证书', 'success');
        }

        // 生成检测总结
        function generateSummary() {
            elements.summarySection.style.display = 'block';
            elements.troubleshootingSection.style.display = 'block';
            
            let summaryHtml = '';
            let troubleshootingHtml = '';
            
            summaryHtml += `<div class="summary-item"><strong>常见网站测试:</strong> ${testResults.commonWebsites ? '完成' : '未完成'}</div>`;
            summaryHtml += `<div class="summary-item"><strong>基础网络检测:</strong> ${testResults.basic ? '正常' : '存在异常'}</div>`;
            summaryHtml += `<div class="summary-item"><strong>站点连通性:</strong> ${testResults.site ? '正常' : '连接失败'}</div>`;
            summaryHtml += `<div class="summary-item"><strong>资源连通性:</strong> ${testResults.resource ? '全部成功' : '部分/全部失败'}</div>`;
            summaryHtml += `<div class="summary-item"><strong>网络抖动检测:</strong> ${testResults.latency ? '完成' : '未完成'}</div>`;
            summaryHtml += `<div class="summary-item"><strong>安全与证书检测:</strong> 请按照说明手动查看证书</div>`;
            summaryHtml += `<div class="summary-item"><strong>总体状态:</strong> ${testResults.commonWebsites && testResults.basic && testResults.site && testResults.resource && testResults.latency ? '正常' : '异常'}</div>`;
            
            troubleshootingHtml += `<ul>`;
            troubleshootingHtml += `<li>检查网络连接是否正常,尝试切换Wi-Fi/有线网络</li>`;
            troubleshootingHtml += `<li>清除浏览器缓存和Cookie后重试</li>`;
            troubleshootingHtml += `<li>检查防火墙/杀毒软件是否拦截了对super.boluobao.ai的访问</li>`;
            troubleshootingHtml += `<li>尝试更换DNS服务器(如8.8.8.8或223.5.5.5)</li>`;
            troubleshootingHtml += `<li>如果正在使用VPN,请尝试连接其他区域节点</li>`;
            troubleshootingHtml += `<li>尝试更换VPS节点,选择延迟更低的服务器</li>`;
            troubleshootingHtml += `</ul>`;
            
            elements.summaryContent.innerHTML = summaryHtml;
            elements.troubleshootingContent.innerHTML = troubleshootingHtml;
            
            log('检测总结已生成', 'info');
        }

        // 清理浏览器缓存的辅助函数
        async function clearBrowserCache() {
            log('🧹 正在清理缓存...', 'info');
            try {
                // 1. 清理 performance 记录
                if (performance.clearResourceTimings) {
                    performance.clearResourceTimings();
                }
                
                // 2. 尝试强制请求随机资源来破坏缓存
                for (let i = 0; i < 3; i++) {
                    try {
                        const randomUrl = TARGET_URL + '/?clearCache=' + Math.random() + '&t=' + Date.now();
                        const img = new Image();
                        await new Promise((resolve) => {
                            img.onload = resolve;
                            img.onerror = resolve;
                            img.src = randomUrl;
                            setTimeout(resolve, 500);
                        });
                    } catch (e) {
                        // 忽略错误
                    }
                    await new Promise(r => setTimeout(r, 100));
                }
                
                log('✅ 缓存清理完成', 'success');
            } catch (e) {
                log('⚠️ 缓存清理跳过', 'warning');
            }
        }
        
        // 主检测流程
        async function runAllTests() {
            if (isRunning) return;
            
            isRunning = true;
            elements.startTestBtn.disabled = true;
            elements.stopTestBtn.disabled = false;
            elements.logContainer.innerHTML = '';
            updateProgress(0);
            
            try {
                // 1. 常见网站连通信测试前清理缓存
                log('═══ 开始第1项:常见网站连通信测试 ═══', 'info');
                await clearBrowserCache();
                await new Promise(r => setTimeout(r, 500));
                await runCommonWebsitesTests();
                
                // 2. 基础网络信息检测前清理缓存
                log('═══ 开始第2项:基础网络信息检测 ═══', 'info');
                await clearBrowserCache();
                await new Promise(r => setTimeout(r, 500));
                await runBasicNetworkTests();
                
                // 3. 站点连通性检测前清理缓存
                log('═══ 开始第3项:站点连通性检测 ═══', 'info');
                await clearBrowserCache();
                await new Promise(r => setTimeout(r, 500));
                await runSiteConnectivityTests();
                
                // 4. 资源连通性检测前清理缓存
                log('═══ 开始第4项:资源连通性检测 ═══', 'info');
                await clearBrowserCache();
                await new Promise(r => setTimeout(r, 500));
                await runResourceTests();
                
                // 5. 网络抖动检测前清理缓存
                log('═══ 开始第5项:网络抖动检测 ═══', 'info');
                await clearBrowserCache();
                await new Promise(r => setTimeout(r, 500));
                await runLatencyTests();
                
                // 6. 安全与证书检测前清理缓存
                log('═══ 开始第6项:安全与证书检测 ═══', 'info');
                await clearBrowserCache();
                await new Promise(r => setTimeout(r, 500));
                await runSecurityTests();
                
                // 生成总结
                generateSummary();
                
                updateProgress(100);
                log('✅ 所有检测已完成', 'success');
            } catch (error) {
                log(`❌ 检测过程中发生错误: ${error.message}`, 'error');
            } finally {
                isRunning = false;
                elements.startTestBtn.disabled = false;
                elements.stopTestBtn.disabled = true;
            }
        }

        // 停止检测
        function stopTests() {
            if (abortController) {
                abortController.abort();
            }
            isRunning = false;
            elements.startTestBtn.disabled = false;
            elements.stopTestBtn.disabled = true;
            log('🛑 检测已手动停止', 'warning');
        }

        // 清除日志
        function clearLog() {
            logEntries.length = 0;
            elements.logContainer.innerHTML = '';
            log('日志已清空', 'info');
        }

        // 手动解析HTML并检测资源
        async function parseHtmlAndDetect() {
            const html = elements.htmlInput.value.trim();
            if (!html) {
                log('请先粘贴HTML源码', 'warning');
                return;
            }

            log('开始手动解析HTML...', 'info');

            // 解析HTML获取资源链接
            const parsedResources = parseResourcesFromHTML(html, TARGET_URL);

            if (parsedResources.length === 0) {
                log('未能从HTML中解析到资源链接', 'warning');
                return;
            }

            log(`✓ 从HTML中解析到 ${parsedResources.length} 个资源链接`, 'success');
            parsedResources.forEach((res, idx) => {
                log(`  [${idx + 1}] ${res.name}: ${res.url}`, 'info');
            });

            // 更新状态
            document.getElementById('resourceStatus').className = 'section-status status-info';
            document.getElementById('resourceStatus').textContent = '检测中 (手动)';

            // 创建资源检测上下文
            const requests = new Map();
            let totalTime = 0;
            const detectStartTime = performance.now();

            // 添加首页作为第一个资源
            requests.set(TARGET_URL + '/', {
                name: '首页',
                fullUrl: TARGET_URL + '/',
                type: 'document',
                initiator: 'manual',
                category: 'html',
                startTime: 0,
                duration: 0,
                statusText: '200',
                statusClass: 'status-ok',
                fromCache: false
            });

            // 更新表格显示函数
            function updateTable() {
                const tableBody = document.getElementById('networkTableBody');
                tableBody.innerHTML = '';
                
                const requestArray = Array.from(requests.values());
                
                for (const req of requestArray) {
                    // 瀑布图位置和宽度
                    const waterfallLeft = (req.startTime / 60000) * 100;
                    const waterfallWidth = Math.max((req.duration / 60000) * 100, 0.5);
                    
                    // 时间格式化
                    const timeText = req.duration > 0 ? Math.round(req.duration) + ' ms' : '-';
                    
                    const row = document.createElement('tr');
                    row.innerHTML = `
                        <td class="col-name" title="${req.fullUrl}">${req.name}</td>
                        <td class="col-status ${req.statusClass}">${req.statusText}</td>
                        <td class="col-type">${req.type}</td>
                        <td class="col-initiator">${req.initiator}</td>
                        <td class="col-time">${timeText}</td>
                        <td class="col-waterfall">
                            <div class="waterfall-cell">
                                <div class="waterfall-bar ${req.category}" 
                                     style="left: ${waterfallLeft}%; width: ${waterfallWidth}%;">
                                </div>
                            </div>
                        </td>
                    `;
                    
                    tableBody.appendChild(row);
                }
                
                // 更新统计
                document.getElementById('networkStats').style.display = 'flex';
                document.getElementById('requestCount').textContent = requestArray.length + ' 个请求';
                document.getElementById('loadTime').textContent = '完成时间 ' + Math.round(totalTime) + ' ms';
            }

            updateTable();

            // 逐个请求资源
            for (let i = 0; i < parsedResources.length; i++) {
                const resource = parsedResources[i];
                const startTime = performance.now() - detectStartTime;

                try {
                    const requestStartTime = performance.now();

                    if (resource.category === 'image') {
                        await new Promise((resolve, reject) => {
                            const img = new Image();
                            const timeout = setTimeout(() => reject(new Error('Timeout')), 10000);
                            img.onload = () => {
                                clearTimeout(timeout);
                                resolve();
                            };
                            img.onerror = () => {
                                clearTimeout(timeout);
                                reject(new Error('Load failed'));
                            };
                            img.src = resource.url + '?t=' + Date.now();
                        });
                    } else {
                        const response = await fetchWithTimeout(resource.url, {
                            method: 'GET',
                            timeout: 10000
                        });

                        if (!response.ok && response.type !== 'opaque' && response.type !== 'opaqueredirect') {
                            throw new Error('HTTP ' + response.status);
                        }
                    }

                    const endTime = performance.now();
                    const duration = endTime - requestStartTime;

                    // 添加到请求列表
                    const url = resource.url;
                    if (!requests.has(url)) {
                        let name = url.split('/').pop() || url;
                        if (name.length > 40) {
                            name = name.substring(0, 37) + '...';
                        }

                        const req = {
                            name: name,
                            fullUrl: url,
                            type: resource.type,
                            initiator: 'manual',
                            category: resource.category,
                            startTime: startTime,
                            duration: duration,
                            statusText: '200',
                            statusClass: 'status-ok',
                            fromCache: false
                        };

                        requests.set(url, req);
                        totalTime = Math.max(totalTime, startTime + duration);
                        updateTable();
                    }

                    log(`✓ 成功加载: ${resource.name} (${Math.round(duration)}ms)`, 'success');

                } catch (error) {
                    log(`✗ 加载失败: ${resource.name}`, 'error');
                }

                // 间隔一点时间
                await new Promise(r => setTimeout(r, 200));
            }

            // 完成
            document.getElementById('resourceStatus').className = 'section-status status-success';
            document.getElementById('resourceStatus').textContent = '完成';
            
            log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'info');
            log('资源详情列表:', 'info');
            log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'info');

            const requestArray = Array.from(requests.values());
            requestArray.forEach((req, index) => {
                const logMsg = `[${index + 1}] 名称: ${req.name} | 类型: ${req.type} | 时间: ${req.duration}ms | URL: ${req.fullUrl}`;
                log(logMsg, 'info');
            });

            log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'info');
            log(`✓ 手动资源检测完成,共检测 ${requests.size} 个资源`, 'success');
        }

        // 事件监听
        elements.startTestBtn.addEventListener('click', runAllTests);
        elements.stopTestBtn.addEventListener('click', stopTests);
        elements.clearLogBtn.addEventListener('click', clearLog);
        elements.copyLogBtn.addEventListener('click', copyLog);
        elements.exportLogBtn.addEventListener('click', exportLog);

        // 初始化
        window.addEventListener('load', () => {
            log('工具已加载完成,点击"开始全面检测"按钮开始诊断', 'info');
        });
    </script>
</body>
</html>
相关推荐
IMPYLH3 小时前
Linux 的 wc 命令
linux·运维·服务器·前端·bash
ElevenS_it1883 小时前
Redis监控实战:内存使用+命中率+连接数三类核心指标接入Zabbix+分级告警完整配置方案
运维·网络·redis·mybatis·zabbix
happybasic4 小时前
Python库升级标准流程~
linux·前端·python
川冰ICE4 小时前
前端工程化深度实战:从Webpack5到Vite5的构建工具演进与选型决策
前端
CDwenhuohuo4 小时前
优惠券组件直接用 uview plus
前端·javascript·vue.js
用户74090472362754 小时前
我用 curl 排查了一次 OpenAI-compatible API 连接失败:401、403、404 分别怎么定位
前端
kft13144 小时前
XSS深度剖析:从弹窗到持久化窃取Cookie
前端·web安全·xss·安全测试
烬羽4 小时前
《前端三权分立:HTML、CSS、JS为什么不能“乱搞”》
前端
oo哦哦4 小时前
搜索矩阵系统的最短路密码:用Dijkstra算法和网络流理论,解释为什么你做了1000个关键词,流量还不如别人30个
网络·算法·矩阵