Cloudflare Workers Proxy Code (V2版)

person 元章 event 2026-02-23 local_offer 代码 chat 评论
代码展示
/**
 * 私有网关终极版 - Ultimate Version
 * 1. 极简 UI(仅保留直接访问)
 * 2. 静态资源强缓存拦截(大幅减少 Worker 请求消耗)
 * 3. 终极修复:完美支持 YouTube 视频流加载、GitHub SPA 搜索
 * 4. 终极修复:解决 Google/Cloudflare "异常流量" 防火墙拦截
 */

const CONFIG = {
    USER: "admin",
    PASS: "admin",
    TITLE: "私有安全网关",
    AUTH_COOKIE: "p_token_final",
    AUTH_VALUE: "v_ultimate_safe"
};

export default {
    async fetch(request, env, ctx) {
        const url = new URL(request.url);
        
        // --- 1. 拦截无效请求 ---
        if (url.pathname === '/favicon.ico' || url.pathname === '/robots.txt') {
            return new Response(null, { status: 204 });
        }

        const cookieHeader = request.headers.get("Cookie") || "";

        // --- 2. 登录处理接口 ---
        if (url.pathname === "/--login--" && request.method === "POST") {
            try {
                const body = await request.json();
                if (body.u === CONFIG.USER && body.p === CONFIG.PASS) {
                    return new Response("OK", {
                        headers: { 
                            "Set-Cookie": `${CONFIG.AUTH_COOKIE}=${CONFIG.AUTH_VALUE}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=2592000`,
                            "Cache-Control": "no-store, no-cache, must-revalidate"
                        }
                    });
                }
                return new Response("Fail", { status: 401 });
            } catch (e) {
                return new Response("Error", { status: 400 });
            }
        }

        // --- 3. 权限校验 ---
        const isAuthed = cookieHeader.includes(`${CONFIG.AUTH_COOKIE}=${CONFIG.AUTH_VALUE}`);
        if (!isAuthed) {
            return new Response(getLoginUI(), { 
                headers: { 
                    "Content-Type": "text/html;charset=UTF-8",
                    "Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate"
                } 
            });
        }

        // --- 4. 后台书签存储 ---
        if (url.pathname === "/--links--" && request.method === "POST") {
            const body = await request.text();
            if (env.LINKS_KV) await env.LINKS_KV.put("user_links", body);
            return new Response("Saved", { headers: { "Cache-Control": "no-store" } });
        }

        // --- 5. 首页直出渲染 ---
        if (url.pathname === "/" && url.search === "") {
            let linksData = "[]";
            if (env.LINKS_KV) {
                try { linksData = await env.LINKS_KV.get("user_links") || "[]"; } catch(e) {}
            }
            return new Response(getIndexUI(linksData), { 
                headers: { "Content-Type": "text/html;charset=UTF-8", "Cache-Control": "no-store, no-cache, must-revalidate" } 
            });
        }

        // --- 6. 代理路由逻辑 (修复 YouTube/GitHub SPA 相对路径丢失问题) ---
        let targetStr = url.pathname.slice(1) + url.search;
        let finalUrl;

        if (targetStr.startsWith('http://') || targetStr.startsWith('https://')) {
            finalUrl = targetStr;
        } else {
            const firstSegment = targetStr.split('/')[0].split('?')[0];
            const isDomain = /^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(:\d+)?$/.test(firstSegment) || 
                             /^\d{1,3}(\.\d{1,3}){3}(:\d+)?$/.test(firstSegment) || 
                             /^localhost(:\d+)?$/.test(firstSegment);
            
            if (isDomain) {
                finalUrl = 'https://' + targetStr;
            } else {
                // 【核心修复 1】: 内部无缝重写 (Internal Rewrite) 替代 302 重定向
                // 完美保留 POST 请求的数据包,彻底解决 YouTube 加载白屏和 GitHub 搜索 1003 报错
                const referer = request.headers.get('Referer');
                if (referer) {
                    try {
                        const refUrl = new URL(referer);
                        const match = refUrl.pathname.match(/^\/(https?:\/\/[^\/]+)/);
                        if (match) {
                            finalUrl = match[1] + '/' + targetStr;
                        }
                    } catch(e) {}
                }
                if (!finalUrl) {
                    return new Response("⚠️ 代理请求解析失败:缺少目标域名。请返回首页重新输入完整网址。", { status: 400 });
                }
            }
        }
        
        try {
            const targetUrl = new URL(finalUrl);
            const cleanHeaders = new Headers();
            
            // 【核心修复 2】: 尊重原生 Sec-Fetch-* 头,不强制篡改,解决 Google 检测到异常流量导致拦截
            const whitelist = ['user-agent', 'accept', 'accept-language', 'content-type', 'cookie', 'range', 'dnt', 'upgrade-insecure-requests'];
            
            for (let [k, v] of request.headers.entries()) {
                let key = k.toLowerCase();
                // 放行白名单和所有合法的 Sec- 客户端原生指纹特征
                if ((whitelist.includes(key) || key.startsWith('sec-')) && !key.startsWith('cf-')) {
                    if (key === 'cookie' && v) {
                        const filtered = v.split(';').filter(c => !c.trim().startsWith(CONFIG.AUTH_COOKIE)).join(';');
                        if (filtered) cleanHeaders.set(k, filtered);
                    } else {
                        cleanHeaders.set(k, v);
                    }
                }
            }

            cleanHeaders.set("Origin", targetUrl.origin);
            cleanHeaders.set("Referer", targetUrl.href);
            
            // 透传真实客户端 IP,避免 Cloudflare 节点群体污染导致被 Google 限流
            const clientIP = request.headers.get('cf-connecting-ip');
            if (clientIP) cleanHeaders.set('X-Forwarded-For', clientIP);

            const response = await fetch(targetUrl.href, {
                method: request.method,
                headers: cleanHeaders,
                body: (request.method !== "GET" && request.method !== "HEAD") ? request.body : null,
                redirect: "manual"
            });

            if ([301, 302, 303, 307, 308].includes(response.status)) {
                const loc = response.headers.get("Location");
                if (loc) {
                    const absLoc = new URL(loc, targetUrl.href).href;
                    return Response.redirect(`https://${url.host}/${absLoc}`, response.status);
                }
            }

            let resHeaders = new Headers(response.headers);
            const contentType = response.headers.get("content-type") || "";
            
            resHeaders.set("Access-Control-Allow-Origin", "*");
            resHeaders.delete("content-security-policy");
            resHeaders.delete("content-security-policy-report-only");
            resHeaders.delete("x-frame-options");
            resHeaders.delete("cross-origin-embedder-policy"); 
            resHeaders.delete("cross-origin-opener-policy");

            if (typeof response.headers.getSetCookie === 'function') {
                resHeaders.delete("set-cookie");
                for (const c of response.headers.getSetCookie()) {
                    let rewritten = c.replace(/Domain=[^;]+/ig, '').replace(/SameSite=Strict/ig, 'SameSite=Lax');
                    resHeaders.append("set-cookie", rewritten);
                }
            }

            if (request.method === "GET" && contentType.match(/(image|css|javascript|font)/i)) {
                resHeaders.set("Cache-Control", "public, max-age=2592000");
            }

            if (contentType.includes("text/html")) {
                return new HTMLRewriter()
                    .on("head", {
                        element(el) {
                            el.append(`
                            <script>
                            (function() {
                                const proxyBase = window.location.origin + "/";
                                const targetOrigin = "${targetUrl.origin}";
                                
                                // 【核心修复 3】强行干掉 Service Worker,防止 YouTube 本地缓存脚本阻断网络
                                if (navigator.serviceWorker) {
                                    navigator.serviceWorker.getRegistrations().then(regs => regs.forEach(r => r.unregister()));
                                }

                                const wrap = (u) => {
                                    if (!u || typeof u !== 'string' || u.startsWith('data:') || u.startsWith('javascript:') || u.startsWith('blob:') || u.startsWith('#')) return u;
                                    try {
                                        const abs = new URL(u, targetOrigin).href;
                                        if (!abs.startsWith(proxyBase)) return proxyBase + abs;
                                    } catch(e) {}
                                    return u;
                                };

                                // 深度重写 Fetch 请求,捕获 YouTube 特有的 Request 对象发起逻辑
                                const origFetch = window.fetch;
                                window.fetch = async (input, init) => {
                                    try {
                                        if (input instanceof Request) {
                                            return origFetch(new Request(wrap(input.url), input), init);
                                        }
                                        return origFetch(wrap(input.toString()), init);
                                    } catch(e) {
                                        return origFetch(input, init);
                                    }
                                };

                                const _open = XMLHttpRequest.prototype.open;
                                XMLHttpRequest.prototype.open = function() {
                                    if (arguments[1]) arguments[1] = wrap(arguments[1].toString());
                                    return _open.apply(this, arguments);
                                };
                            })();
                            </script>
                            `, { html: true });
                        }
                    })
                    .on("a", new TagHandler("href", url.host, targetUrl))
                    .on("form", new TagHandler("action", url.host, targetUrl))
                    .on("link", new TagHandler("href", url.host, targetUrl))
                    .on("img", new TagHandler("src", url.host, targetUrl))
                    .on("script", new TagHandler("src", url.host, targetUrl))
                    .transform(new Response(response.body, { status: response.status, headers: resHeaders }));
            }

            return new Response(response.body, { status: response.status, headers: resHeaders });

        } catch (e) {
            return new Response("⚠️ 代理请求失败: " + e.message, { status: 500 });
        }
    }
};

class TagHandler {
    constructor(attr, proxyHost, targetUrl) { this.attr = attr; this.proxyHost = proxyHost; this.targetUrl = targetUrl; }
    element(el) {
        let val = el.getAttribute(this.attr);
        if (val && !val.startsWith("data:") && !val.startsWith("javascript:") && !val.startsWith("#")) {
            try { el.setAttribute(this.attr, "https://" + this.proxyHost + "/" + new URL(val, this.targetUrl.href).href); } catch (e) {}
        }
        el.removeAttribute("integrity");
        el.removeAttribute("nonce");
    }
}

function getIndexUI(linksData) {
    return `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>${CONFIG.TITLE}</title>
    <style>
        :root { --bg: #0f172a; --card: #1e293b; --text: #f8fafc; --accent: #38bdf8; --danger: #ef4444; }
        body { background: var(--bg); color: var(--text); font-family: system-ui, sans-serif; display: flex; flex-direction: column; align-items: center; min-height: 100vh; margin: 0; padding-top: 5vh; }
        .container { width: 90%; max-width: 650px; text-align: center; }
        .search-card { background: var(--card); padding: 2.5rem; border-radius: 1.5rem; box-shadow: 0 20px 50px rgba(0,0,0,0.3); margin-bottom: 2rem; }
        h1 { font-size: 1.8rem; margin-bottom: 1.5rem; color: var(--accent); }
        .input-box { width: 100%; padding: 14px 25px; border-radius: 50px; border: 1px solid #334155; background: #0f172a; color: #fff; font-size: 1rem; outline: none; box-sizing: border-box; margin-bottom: 1.5rem; }
        .btn-grid { display: flex; justify-content: center; }
        button { width: 100%; max-width: 250px; padding: 14px 10px; border-radius: 30px; border: none; cursor: pointer; font-weight: 700; font-size: 1rem; transition: 0.2s; background: var(--accent); color: #0f172a; }
        button:hover { opacity: 0.9; transform: translateY(-2px); box-shadow: 0 5px 15px rgba(56,189,248,0.3); }
        .nav-section { width: 100%; text-align: left; }
        .nav-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; padding: 0 10px; border-left: 4px solid var(--accent); }
        .nav-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; }
        .nav-item { position: relative; background: var(--card); padding: 15px; border-radius: 12px; text-align: center; cursor: pointer; border: 1px solid transparent; transition: 0.3s; }
        .nav-item:hover { border-color: var(--accent); background: #2d3a4f; }
        .nav-item .delete-btn { position: absolute; top: 5px; right: 5px; color: var(--danger); font-size: 14px; width: 22px; height: 22px; display: none; background: rgba(0,0,0,0.5); border-radius: 50%; line-height: 20px; }
        .nav-item:hover .delete-btn { display: block; }
        .footer { font-size: 0.75rem; color: #64748b; margin-top: 4rem; text-align: center; }
        #status { font-size: 0.8rem; color: var(--accent); margin-left: 10px; display: none; }
    </style>
</head>
<body>
    <div class="container">
        <div class="search-card">
            <h1>🌐 ${CONFIG.TITLE}</h1>
            <input type="text" id="q" class="input-box" placeholder="输入目标 URL 或域名..." autofocus>
            <div class="btn-grid">
                <button onclick="go()">🚀 直 接 访 问</button>
            </div>
        </div>
        <div class="nav-section">
            <div class="nav-header">
                <h3 style="margin:0">🔖 个人书签中心 <span id="status">同步成功</span></h3>
                <span style="font-size:0.8rem; color:var(--accent); cursor:pointer" onclick="addLink()">+ 永久添加</span>
            </div>
            <div id="navGrid" class="nav-grid"></div>
        </div>
    </div>
    <div class="footer">
        Cloudflare KV 云端同步已激活 • Worker 全面兼容 SPA 修复版
    </div>
    <script>
        function go() {
            let v = document.getElementById('q').value.trim();
            if(!v) return;
            if(v.toLowerCase() === 'google') v = 'google.com';
            if(v.toLowerCase() === 'github') v = 'github.com';
            if(v.toLowerCase() === 'youtube') v = 'youtube.com';
            let u = v.startsWith('http') ? v : 'https://' + v;
            location.href = "/" + u;
        }
        document.getElementById('q').onkeypress=e=>{ if(e.key==='Enter') go() };

        let links = ${linksData};

        async function saveLinks() {
            showStatus('正在保存云端...');
            try {
                await fetch('/--links--', { method: 'POST', body: JSON.stringify(links) });
                showStatus('云端已更新', 2000);
            } catch(e) { showStatus('保存失败', 3000); }
        }

        function renderLinks() {
            const grid = document.getElementById('navGrid');
            grid.innerHTML = links.length ? '' : '<div style="grid-column:1/-1;text-align:center;color:#64748b;padding:20px;">点击“+ 永久添加”开始建立云端书签</div>';
            links.forEach((item, i) => {
                const div = document.createElement('div');
                div.className = 'nav-item';
                div.onclick = () => location.href = "/" + item.url;
                div.innerHTML = \`<div>\${item.name}</div><div class="delete-btn" onclick="event.stopPropagation(); deleteLink(\${i})">×</div>\`;
                grid.appendChild(div);
            });
        }

        function addLink() {
            const name = prompt("网站名称:");
            let url = prompt("网址 (如 google.com):");
            if(name && url) {
                if(!url.startsWith('http')) url = 'https://' + url;
                links.push({ name, url });
                renderLinks();
                saveLinks();
            }
        }

        function deleteLink(i) {
            if(confirm('删除此云端书签?')) {
                links.splice(i, 1);
                renderLinks();
                saveLinks();
            }
        }

        function showStatus(msg, ms) {
            const s = document.getElementById('status');
            s.innerText = msg; s.style.display = 'inline';
            if(ms) setTimeout(() => s.style.display = 'none', ms);
        }
        
        renderLinks();
    </script>
</body>
</html>`;
}

function getLoginUI() {
    return `<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录</title>
    <style>
        body{background:#0f172a;color:#fff;display:flex;justify-content:center;align-items:center;height:100vh;margin:0} 
        .card{background:#1e293b;padding:2.5rem;border-radius:1.5rem;text-align:center;width:90%;max-width:320px;box-sizing:border-box;box-shadow:0 10px 30px rgba(0,0,0,0.5)} 
        input{width:100%;padding:14px;margin:10px 0;background:#0f172a;border:1px solid #334155;color:#fff;border-radius:30px;text-align:center;box-sizing:border-box;outline:none;font-size:16px} 
        button{width:100%;padding:14px;margin-top:15px;background:#38bdf8;border:none;border-radius:30px;cursor:pointer;font-weight:700;color:#0f172a;font-size:16px;transition:0.2s} 
        button:hover{opacity:0.9;transform:translateY(-1px)}
    </style>
</head>
<body>
    <div class="card">
        <h2 style="margin-top:0;margin-bottom:20px;color:#38bdf8">🔒 身份验证</h2>
        <input id="u" placeholder="请输入用户名" autocomplete="off">
        <input id="p" type="password" placeholder="请输入密码" onkeypress="if(event.key==='Enter'){ l(); return false; }">
        <button id="btn" onclick="l()">进 入 网 关</button>
    </div>
    <script>
        async function l(){
            const u = document.getElementById('u').value.trim();
            const p = document.getElementById('p').value.trim();
            const btn = document.getElementById('btn');
            
            if(!u || !p) return alert('⚠️ 用户名和密码不能为空');
            
            btn.innerText = '正在验证...';
            try {
                const r = await fetch('/--login--', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({u, p})
                });
                
                if (r.ok) {
                    btn.innerText = '验证成功!';
                    btn.style.background = '#4ade80';
                    setTimeout(() => location.href = '/', 300);
                } else {
                    alert('❌ 账号或密码错误,请重试');
                    btn.innerText = '进 入 网 关';
                }
            } catch (e) {
                alert('⚠️ 无法连接到服务器,请检查网络');
                btn.innerText = '进 入 网 关';
            }
        }
    </script>
</body>
</html>`;
}

copyright 版权声明

相关文章

评论