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

Improved Alibaba Cloud CDN detection logic and TraceID display. 优化了阿里云CDN的识别逻辑并显示TraceID。

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

// ==UserScript==
// @name         CDN & Server Info Displayer Enhanced (增强版CDN及服务器信息显示)
// @namespace    http://tampermonkey.net/
// @version      2025.06.11.15
// @description  Improved Alibaba Cloud CDN detection logic and TraceID display. 优化了阿里云CDN的识别逻辑并显示TraceID。
// @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',
        animationDuration: '0.3s',
        minWindowSize: { width: 400, height: 300 },
        excludePatterns: [
            /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) => ({
                provider: 'Cloudflare',
                cache: headers.get('cf-cache-status')?.toUpperCase() || 'N/A',
                pop: headers.get('cf-ray')?.slice(-3).toUpperCase() || 'N/A',
                extra: `Ray ID: ${headers.get('cf-ray') || 'N/A'}`
            })
        },
        'Tencent EdgeOne': {
            serverHeaders: ['TencentEdgeOne'],
            headers: ['eo-cache-status', 'eo-log-uuid'],
            priority: 10,
            getInfo: (headers) => ({
                provider: 'Tencent EdgeOne',
                cache: headers.get('eo-cache-status')?.toUpperCase() || 'N/A',
                pop: 'N/A',
                extra: `Log-UUID: ${headers.get('eo-log-uuid') || 'N/A'}`
            })
        },
        'Fastly': {
            headers: ['x-fastly-request-id', 'x-served-by', 'fastly-debug-digest'],
            priority: 9,
            getInfo: (headers) => ({
                provider: 'Fastly',
                cache: headers.get('x-cache')?.split(',')[0].toUpperCase() || 'N/A',
                pop: headers.get('x-served-by')?.split('-')[0] || 'N/A',
                extra: `ReqID: ${headers.get('x-fastly-request-id') || 'N/A'}`
            })
        },
        'AWS CloudFront': {
            headers: ['x-amz-cf-pop', 'x-amz-cf-id'],
            priority: 9,
            getInfo: (headers) => ({
                provider: 'AWS CloudFront',
                cache: headers.get('x-cache')?.split(' from ')[0].toUpperCase() || 'N/A',
                pop: headers.get('x-amz-cf-pop') || 'N/A',
                extra: `CF ID: ${headers.get('x-amz-cf-id') || 'N/A'}`
            })
        },
        'Qiniu CDN': {
            headers: ['x-qiniu-zone', 'x-qnm-cache', 'x-m-log'],
            priority: 9,
            getInfo: (headers) => ({
                provider: 'Qiniu CDN',
                cache: headers.get('x-qnm-cache')?.toUpperCase() || headers.get('x-cache')?.split(' ')[0].toUpperCase() || 'N/A',
                pop: `Zone ${headers.get('x-qiniu-zone') || 'N/A'}`,
                extra: `ReqID: ${headers.get('x-reqid') || 'N/A'}`
            })
        },
        'Alibaba Cloud CDN': { // [优化]
            headers: ['ali-swift-global-savetime', 'eagleid', 'eagleeye-traceid', 'x-cache-remote', 'x-swift-cachetime'],
            serverHeaders: ['tengine', 'alicdn'],
            priority: 9,
            getInfo: (headers) => {
                let cacheStatus = 'N/A';
                if (headers.get('x-cache-remote')?.includes('HIT')) cacheStatus = 'HIT';
                else if (headers.get('x-cache-remote')?.includes('MISS')) cacheStatus = 'MISS';
                else if (headers.get('x-swift-cachetime')) cacheStatus = 'HIT';
                
                const traceId = headers.get('eagleid') || headers.get('eagleeye-traceid');

                return {
                    provider: 'Alibaba Cloud CDN',
                    cache: cacheStatus,
                    pop: 'N/A', // 鹰眼ID中无明确POP信息
                    extra: `TraceID: ${traceId || 'N/A'}`
                };
            }
        },
        'Tencent Cloud CDN': {
            headers: ['x-cache-lookup', 'x-nws-log-uuid', 'x-cache-remote'],
            serverHeaders: ['nws', 'tencent'],
            priority: 9,
            getInfo: (headers) => ({
                provider: 'Tencent Cloud CDN',
                cache: headers.get('x-cache-lookup')?.toUpperCase() || 'N/A',
                pop: headers.get('x-nws-log-uuid') ? 'Available' : 'N/A',
                extra: null
            })
        },
        'Akamai': {
            headers: ['x-akamai-transformed', 'x-check-cacheable', 'x-cache-key'],
            priority: 9,
            getInfo: (headers) => ({
                provider: 'Akamai',
                cache: (headers.get('x-check-cacheable') || headers.get('x-cache'))?.toUpperCase() || 'N/A',
                pop: 'N/A',
                extra: headers.get('x-akamai-transformed') ? 'Content transformed' : null
            })
        },
        'QUIC.cloud': {
            headers: ['x-qc-cache', 'x-qc-pop', 'x-litespeed-cache'],
            serverHeaders: ['litespeed'],
            priority: 8,
            getInfo: (headers) => ({
                provider: 'QUIC.cloud',
                cache: (headers.get('x-qc-cache') || headers.get('x-litespeed-cache'))?.toUpperCase() || 'N/A',
                pop: headers.get('x-qc-pop')?.toUpperCase() || 'N/A',
                extra: null
            })
        },
        'Vercel Edge Network': {
            headers: ['x-vercel-id', 'x-vercel-cache'],
            priority: 8,
            getInfo: (headers) => {
                let pop = 'N/A';
                const vercelId = headers.get('x-vercel-id');
                if (vercelId) {
                    const parts = vercelId.split('::');
                    if (parts.length > 1) pop = parts[1].split('-')[0].toUpperCase();
                }
                return {
                    provider: 'Vercel Edge Network',
                    cache: headers.get('x-vercel-cache')?.toUpperCase() || 'N/A',
                    pop: pop, extra: null
                };
            }
        },
    };

    function parseInfo(headers) {
        const lowerCaseHeaders = new Map();
        for (const [key, value] of headers.entries()) {
            lowerCaseHeaders.set(key.toLowerCase(), value);
        }

        let detectedProviders = [];
        for (const [cdnName, cdn] of Object.entries(cdnProviders)) {
            let isMatch = false;
            if (cdn.serverHeaders) {
                const serverHeader = lowerCaseHeaders.get('server') || '';
                if (cdn.serverHeaders.some(server => serverHeader.toLowerCase().includes(server.toLowerCase()))) isMatch = true;
            }
            if (!isMatch && cdn.headers?.some(header => lowerCaseHeaders.has(header.toLowerCase()))) isMatch = true;
            if (!isMatch && cdn.check && cdn.check(lowerCaseHeaders)) isMatch = true;

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

        if (detectedProviders.length > 0) {
            detectedProviders.sort((a, b) => b.priority - a.priority);
            return detectedProviders[0];
        }

        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].toUpperCase() || '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: 220px;
            }
            #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: 140px; 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); word-break: break-all; }
            #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';
        const cacheStatus = info.cache.toUpperCase();
        const cacheClass = cacheStatus.includes('HIT') ? 'cache-HIT' : (cacheStatus.includes('MISS') ? 'cache-MISS' : '');
        const providerLabel = info.provider.includes('CDN') || info.provider.includes('Cloud') || info.provider.includes('Edge') ? 'CDN' : 'Server';
        panel.innerHTML = `
            <button class="close-btn" title="Close" onclick="this.parentElement.remove()">×</button>
            <div class="panel-header">CDN & Server Info</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" title="${info.pop}">${info.pop}</span>
            </div>
            ${info.extra ? `<div class="extra-info" title="${info.extra}">${info.extra}</div>` : ''}
        `;
        Object.assign(panel.style, config.initialPosition);
        document.body.appendChild(panel);
        makeDraggable(panel);
    }

    function makeDraggable(element) {
        let isDragging = false, startX = 0, startY = 0, elementX = 0, elementY = 0;
        element.addEventListener('mousedown', dragStart);
        function dragStart(e) {
            if (e.target.classList.contains('close-btn')) return;
            isDragging = true; startX = e.clientX; startY = e.clientY;
            const rect = element.getBoundingClientRect(); elementX = rect.left; elementY = rect.top;
            element.style.transition = 'none';
            document.addEventListener('mousemove', drag); document.addEventListener('mouseup', dragEnd);
        }
        function drag(e) {
            if (!isDragging) return;
            const newX = elementX + e.clientX - startX; const newY = elementY + e.clientY - startY;
            const maxX = window.innerWidth - element.offsetWidth; const maxY = window.innerHeight - element.offsetHeight;
            element.style.left = `${Math.max(0, Math.min(newX, maxX))}px`;
            element.style.top = `${Math.max(0, Math.min(newY, maxY))}px`;
            element.style.right = 'auto'; element.style.bottom = 'auto';
        }
        function dragEnd() {
            isDragging = false;
            element.style.transition = `opacity ${config.animationDuration}, transform ${config.animationDuration}`;
            document.removeEventListener('mousemove', drag); document.removeEventListener('mouseup', dragEnd);
        }
    }

    function isElementVisible(el) {
        if (!el) return false;
        const rect = el.getBoundingClientRect();
        return (rect.width > 1 && rect.height > 1 && getComputedStyle(el).visibility !== 'hidden' && getComputedStyle(el).display !== 'none');
    }

    function shouldShowPanel() {
        if (window !== window.top && (window.innerWidth < config.minWindowSize.width || window.innerHeight < config.minWindowSize.height)) {
            console.log(`[CDN Detector] Iframe too small, skipping.`); return false;
        }
        const url = window.location.href.toLowerCase();
        for (const pattern of config.excludePatterns) {
            if (pattern.test(url)) {
                console.log(`[CDN Detector] URL matches exclude pattern: ${pattern}, skipping.`); return false;
            }
        }
        const captchaSelectors = {
            strict: [/challenge/i, /verify/i, /security check/i],
            lenient: ['[id*="captcha"]', '[class*="captcha"]', '[id*="recaptcha"]', '[class*="recaptcha"]', '[id*="hcaptcha"]', '[class*="hcaptcha"]', '[class*="cf-turnstile"]', '[id*="turnstile"]']
        };
        const pageTitle = document.title.toLowerCase();
        for (const pattern of captchaSelectors.strict) {
             if (pattern.test(pageTitle)) {
                console.log(`[CDN Detector] Page title indicates a challenge page: ${pattern}, skipping.`); return false;
            }
        }
        for (const selector of captchaSelectors.lenient) {
            const element = document.querySelector(selector);
            if (element && isElementVisible(element)) {
                console.log(`[CDN Detector] Found a VISIBLE captcha element: ${selector}, skipping.`); return false;
            }
        }
        const excludeDomains = ['accounts.google.com', 'login.microsoftonline.com', 'challenges.cloudflare.com'];
        const hostname = window.location.hostname.toLowerCase();
        for (const domain of excludeDomains) {
            if (hostname.includes(domain)) {
                console.log(`[CDN Detector] Excluded domain detected: ${domain}, skipping.`); return false;
            }
        }
        return true;
    }

    async function main() {
        if (document.readyState !== "complete") {
            await new Promise(resolve => { window.addEventListener("load", resolve); });
        }
        if (!shouldShowPanel()) {
            return;
        }
        try {
            const response = await fetch(window.location.href, { method: 'HEAD', cache: 'no-cache', redirect: 'follow' });
            const info = parseInfo(response.headers);
            console.log('[CDN Detector] Result:', info);
            createDisplayPanel(info);
        } catch (error) {
            console.warn('[CDN Detector] Failed to fetch headers (CORS or network issue):', error.message);
            createDisplayPanel({
                provider: 'Detection Limited',
                cache: 'N/A', pop: 'N/A',
                extra: 'CORS policy blocked analysis'
            });
        }
    }

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

})();

QingJ © 2025

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