CDN & Server Info Displayer Enhanced (增强版CDN及服务器信息显示)

Enhanced CDN detection with accurate rules for major providers. Displays CDN provider, cache status, server info and POP locations. 增强版CDN检测,支持更多服务商,准确显示CDN提供商、缓存状态、服务器信息和节点位置。

当前为 2025-06-10 提交的版本,查看 最新版本

// ==UserScript==
// @name         CDN & Server Info Displayer Enhanced (增强版CDN及服务器信息显示)
// @namespace    http://tampermonkey.net/
// @version      2025.06.11.05 enhanced
// @description  Enhanced CDN detection with accurate rules for major providers. Displays CDN provider, cache status, server info and POP locations. 增强版CDN检测,支持更多服务商,准确显示CDN提供商、缓存状态、服务器信息和节点位置。
// @author       Claude (Enhanced by AI)
// @license      MIT
// @match        *://*/*
// @grant        GM_addStyle
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // --- 配置项 ---
    const config = {
        initialPosition: { bottom: '15px', right: '15px' },
        panelBgColor: 'rgba(44, 62, 80, 0.95)',  // 增加透明度
        panelTextColor: '#ffffff',
        borderColor: '#3498db',
        opacity: '0.9',
        showDetailed: true,  // 是否显示详细信息
        animationDuration: '0.3s',
        // 过滤配置
        minWindowSize: { width: 400, height: 300 },  // 最小窗口尺寸
        excludePatterns: [
            // 验证码相关
            /captcha/i, /recaptcha/i, /hcaptcha/i, /turnstile/i,
            // 支付相关
            /pay(pal|ment)/i, /checkout/i, /billing/i,
            // 登录(不可用)弹窗
            /login/i, /signin/i, /auth/i, /oauth/i,
            // 广告相关
            /ads/i, /advertisement/i, /doubleclick/i,
            // 社交媒体插件
            /facebook\.com\/plugins/i, /twitter\.com\/widgets/i,
            // 其他小工具
            /widget/i, /embed/i, /popup/i, /modal/i
        ]
    };

    // --- CDN 服务商检测规则 (修正并增强) ---
    const cdnProviders = {
        'Cloudflare': {
            headers: ['cf-ray', 'cf-cache-status', 'cf-request-id'],
            serverHeaders: ['cloudflare'],
            priority: 10,
            getInfo: (headers) => {
                const ray = headers.get('cf-ray');
                const cacheStatus = headers.get('cf-cache-status');
                const datacenter = ray ? ray.slice(-3).toUpperCase() : 'N/A';

                return {
                    provider: 'Cloudflare',
                    cache: cacheStatus?.toUpperCase() || 'N/A',
                    pop: datacenter,
                    extra: headers.get('cf-request-id') ? 'Request ID available' : null
                };
            }
        },

        'Alibaba Cloud CDN': {
            headers: ['ali-swift-global-savetime', 'eagleid', 'x-cache-remote', 'x-swift-cachetime'],
            serverHeaders: ['tengine', 'alicdn'],
            priority: 9,
            getInfo: (headers) => {
                // 阿里云CDN的正确检测规则
                const cacheRemote = headers.get('x-cache-remote');
                const swiftCache = headers.get('x-swift-cachetime');
                const eagleId = headers.get('eagleid');

                let cacheStatus = 'N/A';
                if (cacheRemote) {
                    cacheStatus = cacheRemote.includes('HIT') ? 'HIT' : 'MISS';
                } else if (swiftCache) {
                    cacheStatus = 'HIT';
                }

                // 从EagleEye TraceId提取节点信息
                let pop = 'N/A';
                if (eagleId) {
                    const popMatch = eagleId.match(/(\w{2,3})-/);
                    pop = popMatch ? popMatch[1].toUpperCase() : 'N/A';
                }

                return {
                    provider: 'Alibaba Cloud CDN',
                    cache: cacheStatus,
                    pop: pop,
                    extra: headers.get('ali-swift-global-savetime') ? 'Global acceleration' : null
                };
            }
        },

        'Tencent Cloud CDN': {
            headers: ['x-cache-lookup', 'x-nws-log-uuid', 'x-cache-remote'],
            serverHeaders: ['nws', 'tencent'],
            priority: 9,
            getInfo: (headers) => {
                const cacheLookup = headers.get('x-cache-lookup');
                const cacheRemote = headers.get('x-cache-remote');
                const nwsLog = headers.get('x-nws-log-uuid');

                let cacheStatus = 'N/A';
                if (cacheLookup) {
                    cacheStatus = cacheLookup.split(' ')[0].toUpperCase();
                } else if (cacheRemote?.includes('HIT')) {
                    cacheStatus = 'HIT';
                }

                return {
                    provider: 'Tencent Cloud CDN',
                    cache: cacheStatus,
                    pop: nwsLog ? 'Available' : 'N/A',
                    extra: null
                };
            }
        },

        'AWS CloudFront': {
            headers: ['x-amz-cf-pop', 'x-amz-cf-id', 'x-cache'],
            priority: 9,
            getInfo: (headers) => {
                const pop = headers.get('x-amz-cf-pop');
                const cache = headers.get('x-cache');

                return {
                    provider: 'AWS CloudFront',
                    cache: cache?.split(' from ')[0] || 'N/A',
                    pop: pop || 'N/A',
                    extra: headers.get('x-amz-cf-id') ? 'Request ID available' : null
                };
            }
        },

        'QUIC.cloud': {
            headers: ['x-qc-cache', 'x-qc-pop', 'x-litespeed-cache'],
            serverHeaders: ['litespeed'],
            priority: 8,
            getInfo: (headers) => {
                const qcCache = headers.get('x-qc-cache');
                const lsCache = headers.get('x-litespeed-cache');
                const pop = headers.get('x-qc-pop');

                let cacheStatus = qcCache || lsCache || 'N/A';
                if (cacheStatus !== 'N/A') {
                    cacheStatus = cacheStatus.toUpperCase();
                }

                return {
                    provider: 'QUIC.cloud',
                    cache: cacheStatus,
                    pop: pop?.toUpperCase() || 'N/A',
                    extra: null
                };
            }
        },

        'Fastly': {
            headers: ['x-served-by', 'x-cache', 'fastly-debug-digest'],
            priority: 8,
            getInfo: (headers) => {
                const servedBy = headers.get('x-served-by');
                const cache = headers.get('x-cache');

                return {
                    provider: 'Fastly',
                    cache: cache?.split(',')[0] || 'N/A',
                    pop: servedBy?.split('-')[0] || 'N/A',
                    extra: headers.get('fastly-debug-digest') ? 'Debug info available' : null
                };
            }
        },

        'KeyCDN': {
            headers: ['x-edge-location', 'x-cache'],
            serverHeaders: ['keycdn'],
            priority: 7,
            getInfo: (headers) => ({
                provider: 'KeyCDN',
                cache: headers.get('x-cache') || 'N/A',
                pop: headers.get('x-edge-location') || 'N/A',
                extra: null
            })
        },

        'MaxCDN/StackPath': {
            headers: ['x-served-by', 'x-cache'],
            check: (headers) => {
                const servedBy = headers.get('x-served-by');
                return servedBy && servedBy.includes('maxcdn');
            },
            priority: 7,
            getInfo: (headers) => ({
                provider: 'MaxCDN/StackPath',
                cache: headers.get('x-cache') || 'N/A',
                pop: headers.get('x-served-by')?.split('.')[0] || 'N/A',
                extra: null
            })
        },

        'Vercel Edge Network': {
            headers: ['x-vercel-id', 'x-vercel-cache'],
            priority: 8,
            getInfo: (headers) => {
                const vercelId = headers.get('x-vercel-id');
                const cache = headers.get('x-vercel-cache');

                let pop = 'N/A';
                if (vercelId) {
                    const parts = vercelId.split('::');
                    if (parts.length > 1) {
                        pop = parts[1].split('-')[0].toUpperCase();
                    }
                }

                return {
                    provider: 'Vercel Edge Network',
                    cache: cache?.toUpperCase() || 'N/A',
                    pop: pop,
                    extra: null
                };
            }
        },

        'BunnyCDN': {
            headers: ['cdn-cache', 'cdn-pullzone', 'cdn-requestid'],
            serverHeaders: ['bunnycdn'],
            priority: 7,
            getInfo: (headers) => ({
                provider: 'BunnyCDN',
                cache: headers.get('cdn-cache')?.toUpperCase() || 'N/A',
                pop: headers.get('cdn-pullzone') || 'N/A',
                extra: headers.get('cdn-requestid') ? 'Request ID available' : null
            })
        },

        'Akamai': {
            headers: ['x-akamai-transformed', 'x-check-cacheable', 'x-cache-key'],
            priority: 9,
            getInfo: (headers) => {
                const cache = headers.get('x-check-cacheable') || headers.get('x-cache');
                return {
                    provider: 'Akamai',
                    cache: cache || 'N/A',
                    pop: 'N/A',
                    extra: headers.get('x-akamai-transformed') ? 'Content transformed' : null
                };
            }
        },

        'Azure CDN': {
            headers: ['x-azure-ref', 'x-cache'],
            priority: 8,
            getInfo: (headers) => ({
                provider: 'Azure CDN',
                cache: headers.get('x-cache') || 'N/A',
                pop: headers.get('x-azure-ref')?.split(':')[0] || 'N/A',
                extra: null
            })
        },

        'Google Cloud CDN': {
            headers: ['x-goog-generation', 'x-goog-hash', 'x-cache'],
            serverHeaders: ['gws'],
            priority: 8,
            getInfo: (headers) => ({
                provider: 'Google Cloud CDN',
                cache: headers.get('x-cache') || 'N/A',
                pop: 'N/A',
                extra: headers.get('x-goog-generation') ? 'Object versioning' : null
            })
        },

        'Netlify': {
            headers: ['x-nf-request-id'],
            serverHeaders: ['netlify'],
            priority: 7,
            getInfo: (headers) => ({
                provider: 'Netlify Edge',
                cache: 'N/A',
                pop: 'N/A',
                extra: 'Netlify Edge Network'
            })
        },

        'jsDelivr': {
            headers: ['x-served-by'],
            check: (headers) => {
                const servedBy = headers.get('x-served-by');
                return servedBy && servedBy.includes('jsdelivr');
            },
            priority: 6,
            getInfo: (headers) => ({
                provider: 'jsDelivr CDN',
                cache: 'N/A',
                pop: headers.get('x-served-by')?.split('-')[0] || 'N/A',
                extra: 'Open Source CDN'
            })
        }
    };

    /**
     * 从headers中解析CDN或服务器信息
     */
    function parseInfo(headers) {
        const lowerCaseHeaders = new Map();
        for (const [key, value] of headers.entries()) {
            lowerCaseHeaders.set(key.toLowerCase(), value);
        }

        let detectedProviders = [];

        // 检测所有匹配的CDN提供商
        for (const [cdnName, cdn] of Object.entries(cdnProviders)) {
            let isMatch = false;

            // 检查特定的头部字段
            if (cdn.headers?.some(header => lowerCaseHeaders.has(header.toLowerCase()))) {
                isMatch = true;
            }

            // 检查服务器头部
            if (cdn.serverHeaders) {
                const serverHeader = lowerCaseHeaders.get('server') || '';
                if (cdn.serverHeaders.some(server =>
                    serverHeader.toLowerCase().includes(server.toLowerCase()))) {
                    isMatch = true;
                }
            }

            // 自定义检查函数
            if (cdn.check && cdn.check(lowerCaseHeaders)) {
                isMatch = true;
            }

            if (isMatch) {
                const info = cdn.getInfo(lowerCaseHeaders);
                detectedProviders.push({
                    ...info,
                    priority: cdn.priority || 5
                });
            }
        }

        // 如果检测到多个CDN,选择优先级最高的
        if (detectedProviders.length > 0) {
            detectedProviders.sort((a, b) => b.priority - a.priority);
            return detectedProviders[0];
        }

        // 如果没有匹配到已知CDN,尝试显示服务器信息
        const server = lowerCaseHeaders.get('server');
        const xCache = lowerCaseHeaders.get('x-cache');
        const via = lowerCaseHeaders.get('via');

        if (server || xCache || via) {
            return {
                provider: server || 'Unknown Server',
                cache: xCache?.split(' ')[0] || 'N/A',
                pop: via ? 'Proxy detected' : 'N/A',
                extra: via ? `Via: ${via}` : null
            };
        }

        return {
            provider: 'Direct Connection',
            cache: 'N/A',
            pop: 'N/A',
            extra: 'No CDN detected'
        };
    }

    /**
     * 创建样式
     */
    function addStyles() {
        GM_addStyle(`
            #cdn-info-panel-enhanced {
                position: fixed;
                z-index: 99999;
                padding: 12px 16px;
                border-radius: 12px;
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                font-size: 13px;
                color: ${config.panelTextColor};
                background: ${config.panelBgColor};
                backdrop-filter: blur(10px);
                border: 1px solid ${config.borderColor};
                box-shadow: 0 8px 32px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.1);
                cursor: move;
                user-select: none;
                transition: opacity ${config.animationDuration}, transform ${config.animationDuration};
                opacity: ${config.opacity};
                transform: scale(1);
                min-width: 200px;
            }

            #cdn-info-panel-enhanced:hover {
                opacity: 1;
                transform: scale(1.02);
            }

            #cdn-info-panel-enhanced .panel-header {
                font-weight: 600;
                font-size: 14px;
                margin-bottom: 8px;
                color: ${config.borderColor};
                text-align: center;
                border-bottom: 1px solid rgba(52, 152, 219, 0.3);
                padding-bottom: 6px;
            }

            #cdn-info-panel-enhanced .info-line {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin: 6px 0;
                padding: 2px 0;
            }

            #cdn-info-panel-enhanced .info-label {
                font-weight: 500;
                color: #a9cce3;
                font-size: 12px;
                text-transform: uppercase;
                letter-spacing: 0.5px;
            }

            #cdn-info-panel-enhanced .info-value {
                font-weight: 600;
                color: #ffffff;
                max-width: 120px;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                text-align: right;
            }

            #cdn-info-panel-enhanced .extra-info {
                font-size: 11px;
                color: #95a5a6;
                font-style: italic;
                text-align: center;
                margin-top: 8px;
                padding-top: 6px;
                border-top: 1px solid rgba(149, 165, 166, 0.2);
            }

            #cdn-info-panel-enhanced .cache-hit {
                color: #2ecc71 !important;
            }

            #cdn-info-panel-enhanced .cache-miss {
                color: #e74c3c !important;
            }

            #cdn-info-panel-enhanced .close-btn {
                position: absolute;
                top: -8px;
                right: -8px;
                width: 20px;
                height: 20px;
                border-radius: 50%;
                background: #e74c3c;
                color: white;
                border: none;
                cursor: pointer;
                font-size: 12px;
                display: none;
                align-items: center;
                justify-content: center;
                transition: all 0.2s;
            }

            #cdn-info-panel-enhanced:hover .close-btn {
                display: flex;
            }

            #cdn-info-panel-enhanced .close-btn:hover {
                background: #c0392b;
                transform: scale(1.1);
            }
        `);
    }

    /**
     * 创建并显示信息面板
     */
    function createDisplayPanel(info) {
        if (!info || document.getElementById('cdn-info-panel-enhanced')) return;

        addStyles();

        const panel = document.createElement('div');
        panel.id = 'cdn-info-panel-enhanced';

        // 根据缓存状态添加样式类
        let cacheClass = '';
        if (info.cache.toLowerCase().includes('hit')) {
            cacheClass = 'cache-hit';
        } else if (info.cache.toLowerCase().includes('miss')) {
            cacheClass = 'cache-miss';
        }

        const providerLabel = info.provider.includes('CDN') ||
                              info.provider.includes('Cloud') ||
                              info.provider.includes('Edge') ? 'CDN' : 'Server';

        panel.innerHTML = `
            <button class="close-btn" onclick="this.parentElement.remove()">×</button>
            <div class="panel-header">CDN Detector</div>
            <div class="info-line">
                <span class="info-label">${providerLabel}</span>
                <span class="info-value" title="${info.provider}">${info.provider}</span>
            </div>
            <div class="info-line">
                <span class="info-label">Cache</span>
                <span class="info-value ${cacheClass}">${info.cache}</span>
            </div>
            <div class="info-line">
                <span class="info-label">POP</span>
                <span class="info-value">${info.pop}</span>
            </div>
            ${info.extra ? `<div class="extra-info">${info.extra}</div>` : ''}
        `;

        // 设置初始位置
        Object.assign(panel.style, config.initialPosition);
        document.body.appendChild(panel);

        // 使面板可拖拽
        makeDraggable(panel);

        // 添加入场动画
        requestAnimationFrame(() => {
            panel.style.opacity = config.opacity;
        });
    }

    /**
     * 使元素可拖拽
     */
    function makeDraggable(element) {
        let isDragging = false;
        let startX = 0, startY = 0;
        let elementX = 0, elementY = 0;

        element.addEventListener('mousedown', dragStart, false);
        document.addEventListener('mousemove', drag, false);
        document.addEventListener('mouseup', dragEnd, false);

        function dragStart(e) {
            if (e.target.classList.contains('close-btn')) return;

            e.preventDefault();
            isDragging = true;

            // 记录鼠标起始位置
            startX = e.clientX;
            startY = e.clientY;

            // 获取元素当前位置
            const rect = element.getBoundingClientRect();
            elementX = rect.left;
            elementY = rect.top;

            // 移除过渡效果,确保拖拽流畅
            element.style.transition = 'none';

            // 添加拖拽时的视觉反馈
            element.style.opacity = '1';
            element.style.transform = 'scale(1.05)';
        }

        function drag(e) {
            if (!isDragging) return;

            e.preventDefault();

            // 计算鼠标移动的距离
            const deltaX = e.clientX - startX;
            const deltaY = e.clientY - startY;

            // 计算元素新位置
            const newX = elementX + deltaX;
            const newY = elementY + deltaY;

            // 边界检测,防止拖出屏幕
            const maxX = window.innerWidth - element.offsetWidth;
            const maxY = window.innerHeight - element.offsetHeight;

            const finalX = Math.max(0, Math.min(newX, maxX));
            const finalY = Math.max(0, Math.min(newY, maxY));

            // 更新元素位置
            element.style.position = 'fixed';
            element.style.left = finalX + 'px';
            element.style.top = finalY + 'px';
            element.style.right = 'auto';
            element.style.bottom = 'auto';
            element.style.transform = 'scale(1.05)';
        }

        function dragEnd() {
            if (!isDragging) return;

            isDragging = false;

            // 恢复过渡效果和正常状态
            element.style.transition = `opacity ${config.animationDuration}, transform ${config.animationDuration}`;
            element.style.transform = 'scale(1)';
            element.style.opacity = config.opacity;
        }
    }

    /**
     * [NEW] 检查是否应该显示CDN检测面板
     */
    function shouldShowPanel() {
        // 1. 检查是否在iframe中
        if (window !== window.top) {
            console.log('[CDN Detector] Running in iframe, checking if should display...');

            // 获取iframe尺寸
            const frameWidth = window.innerWidth;
            const frameHeight = window.innerHeight;

            // iframe太小则不显示
            if (frameWidth < config.minWindowSize.width || frameHeight < config.minWindowSize.height) {
                console.log(`[CDN Detector] Iframe too small (${frameWidth}x${frameHeight}), skipping display`);
                return false;
            }

            // 检查iframe的URL是否匹配排除模式
            const currentUrl = window.location.href.toLowerCase();
            for (const pattern of config.excludePatterns) {
                if (pattern.test(currentUrl)) {
                    console.log(`[CDN Detector] URL matches exclude pattern: ${pattern}, skipping display`);
                    return false;
                }
            }
        }

        // 2. 检查当前页面URL (对主窗口和iframe都适用)
        const currentUrl = window.location.href.toLowerCase();
        for (const pattern of config.excludePatterns) {
            if (pattern.test(currentUrl)) {
                console.log(`[CDN Detector] Current URL matches exclude pattern: ${pattern}, skipping display`);
                return false;
            }
        }

        // 3. 检查页面标题
        const pageTitle = document.title.toLowerCase();
        const titleExcludePatterns = [
            /captcha/i, /verify/i, /challenge/i, /security check/i,
            /human verification/i, /robot check/i
        ];

        for (const pattern of titleExcludePatterns) {
            if (pattern.test(pageTitle)) {
                console.log(`[CDN Detector] Page title matches exclude pattern: ${pattern}, skipping display`);
                return false;
            }
        }

        // 4. 检查常见的验证码容器元素
        const captchaSelectors = [
            '[id*="captcha"]', '[class*="captcha"]',
            '[id*="recaptcha"]', '[class*="recaptcha"]',
            '[id*="hcaptcha"]', '[class*="hcaptcha"]',
            '[class*="cf-turnstile"]', '[id*="turnstile"]'
        ];

        for (const selector of captchaSelectors) {
            if (document.querySelector(selector)) {
                console.log(`[CDN Detector] Found captcha element: ${selector}, skipping display`);
                return false;
            }
        }

        // 5. 检查特殊的域名
        const hostname = window.location.hostname.toLowerCase();
        const excludeDomains = [
            'accounts.google.com',
            'login.microsoftonline.com',
            'auth0.com',
            'okta.com',
            'captcha.com',
            'recaptcha.net',
            'hcaptcha.com',
            'challenges.cloudflare.com'
        ];

        for (const domain of excludeDomains) {
            if (hostname.includes(domain)) {
                console.log(`[CDN Detector] Excluded domain detected: ${domain}, skipping display`);
                return false;
            }
        }

        console.log('[CDN Detector] All checks passed, panel will be displayed.');
        return true;
    }

    async function main() {
        // 等待页面完全加载
        if (document.readyState !== "complete") {
            return new Promise(resolve => {
                window.addEventListener("load", () => {
                    main().then(resolve);
                });
            });
        }
        
        // [MODIFIED] 在执行任何操作前,先检查是否应该显示面板
        if (!shouldShowPanel()) {
            return;
        }

        try {
            console.log('[CDN Detector] Starting detection...');

            // 尝试获取当前页面的响应头
            const response = await fetch(window.location.href, {
                method: 'HEAD',  // 使用HEAD请求减少数据传输
                cache: 'no-cache',
                redirect: 'follow'
            });

            const info = parseInfo(response.headers);
            console.log('[CDN Detector] Detection result:', info);

            createDisplayPanel(info);

        } catch (error) {
            console.warn('[CDN Detector] Failed to fetch headers:', error.message);

            // 降级处理:显示基于已知信息的检测结果
            createDisplayPanel({
                provider: 'Detection Limited',
                cache: 'N/A',
                pop: 'N/A',
                extra: 'CORS policy blocking header access'
            });
        }
    }

    // 启动脚本
    main().catch(error => {
        console.error('[CDN Detector] Initialization failed:', error);
    });

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址