码桶
发现社区成员的开源项目
index.html21 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>反向代理网关 - CORS 跨域中转服务</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
:root {
--bg: #1a1a2e;
--card-bg: #16213e;
--border: #0f3460;
--text: #cdd6f4;
--text-secondary: #a6adc8;
--accent: #89b4fa;
--green: #a6e3a1;
--yellow: #f9e2af;
--red: #f38ba8;
--cyan: #89dceb;
--radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans SC', sans-serif;
padding: 24px;
min-height: 100vh;
line-height: 1.6;
}
.container { max-width: 860px; margin: 0 auto; }
/* ── Header ── */
.header {
text-align: center; margin-bottom: 32px; padding: 36px 24px 28px;
background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius);
}
.header .icon { font-size: 48px; margin-bottom: 12px; }
.header h1 { font-size: 22px; font-weight: 700; color: var(--text); margin-bottom: 6px; }
.header .badge {
display: inline-block; margin-top: 8px; padding: 3px 14px; font-size: 12px; font-weight: 600;
color: var(--green); background: rgba(166,227,161,0.1);
border: 1px solid rgba(166,227,161,0.25); border-radius: 50px;
font-family: 'SF Mono','Fira Code','Courier New',monospace;
}
/* ── Card ── */
.card {
background: var(--card-bg); border: 1px solid var(--border);
border-radius: var(--radius); margin-bottom: 20px; overflow: hidden;
}
.card-header {
padding: 14px 22px; font-size: 14px; font-weight: 600;
background: rgba(137,180,250,0.06); border-bottom: 1px solid var(--border);
display: flex; align-items: center; gap: 8px;
}
.card-header .dot { width: 8px; height: 8px; border-radius: 50%; }
.card-body { padding: 20px 22px; }
/* ── Mode block ── */
.mode-block {
margin-bottom: 24px; padding-bottom: 24px; border-bottom: 1px dashed var(--border);
}
.mode-block:last-child { margin-bottom: 0; padding-bottom: 0; border-bottom: none; }
.mode-label {
font-size: 13px; font-weight: 700; color: var(--accent);
margin-bottom: 6px; display: flex; align-items: center; gap: 6px;
}
.mode-label .icon { font-size: 16px; }
.mode-label .tag {
display: inline-block; font-size: 10px; padding: 1px 8px; border-radius: 4px;
font-weight: 600; text-transform: uppercase; letter-spacing: 0.8px;
}
.tag-standard { background: rgba(137,180,250,0.15); color: var(--accent); }
.tag-disguise { background: rgba(249,226,175,0.15); color: var(--yellow); }
.mode-desc { font-size: 13px; color: var(--text-secondary); margin-bottom: 12px; }
/* ── Input row ── */
.input-row {
display: flex; gap: 6px; align-items: stretch; margin-bottom: 6px;
}
.input-row .prefix {
flex-shrink: 0; display: flex; align-items: center; padding: 0 12px;
font-size: 12px; font-family: 'SF Mono','Fira Code','Courier New',monospace;
color: var(--text-secondary); background: var(--bg);
border: 1px solid var(--border); border-radius: 6px; white-space: nowrap;
}
.sep {
flex-shrink: 0; display: flex; align-items: center; justify-content: center;
width: 20px; font-size: 18px; color: var(--text-secondary); font-family: monospace;
}
input[type="text"] {
padding: 10px 12px; font-size: 13px;
font-family: 'SF Mono','Fira Code','Courier New',monospace;
color: var(--text); background: var(--bg);
border: 1px solid var(--border); border-radius: 6px;
outline: none; transition: border-color 0.2s, box-shadow 0.2s;
min-width: 0;
}
input[type="text"]::placeholder { color: #4a5568; }
input[type="text"]:focus {
border-color: var(--accent); box-shadow: 0 0 0 3px rgba(137,180,250,0.12);
}
input[type="text"].valid { border-color: var(--green); }
input[type="text"].invalid { border-color: var(--red); }
.url-input { flex: 1; }
.domain-input { width: 170px; flex-shrink: 0; }
/* ── Buttons ── */
.btn-inline {
flex-shrink: 0; padding: 0 14px; font-size: 12px; font-weight: 600;
border: none; border-radius: 6px; cursor: pointer; transition: all 0.15s;
display: flex; align-items: center; gap: 4px; white-space: nowrap;
font-family: -apple-system,BlinkMacSystemFont,'Segoe UI','Noto Sans SC',sans-serif;
height: auto;
}
.btn-inline:disabled { opacity: 0.3; cursor: not-allowed; }
.btn-visit { background: var(--accent); color: var(--bg); }
.btn-visit:hover:not(:disabled) { background: #a6c8ff; }
.btn-copy { background: var(--border); color: var(--text-secondary); }
.btn-copy:hover:not(:disabled) { background: var(--accent); color: var(--bg); }
.btn-copy.copied { background: var(--green); color: var(--bg); }
/* ── URL preview ── */
.url-preview {
display: none; align-items: flex-start; gap: 8px; font-size: 12px;
padding: 8px 12px; background: var(--bg); border: 1px solid var(--border);
border-radius: 6px; line-height: 1.5;
}
.url-preview.show { display: flex; }
.url-preview .label {
flex-shrink: 0; color: var(--text-secondary); font-size: 11px; padding-top: 1px;
}
.url-preview .full-url {
font-family: 'SF Mono','Fira Code','Courier New',monospace;
color: var(--cyan); word-break: break-all; overflow-wrap: anywhere;
}
.input-hint { font-size: 11px; color: var(--text-secondary); margin-top: 4px; padding-left: 2px; }
.input-hint.error { color: var(--red); }
.input-hint.success { color: var(--green); }
/* ── Features ── */
.feature-list {
list-style: none; display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
}
.feature-list li {
font-size: 13px; padding: 10px 12px; border-radius: 6px;
background: var(--bg); border: 1px solid var(--border);
display: flex; align-items: flex-start; gap: 8px; line-height: 1.4;
}
.feature-list li .check {
color: var(--green); font-weight: 700; font-size: 15px; flex-shrink: 0; margin-top: 1px;
}
.tip-list { list-style: none; }
.tip-list li {
font-size: 13px; padding: 6px 0; color: var(--text-secondary);
display: flex; align-items: flex-start; gap: 8px;
}
.tip-list li::before { content: '💡'; flex-shrink: 0; font-size: 13px; }
.footer { text-align: center; padding: 10px; font-size: 11px; color: var(--text-secondary); opacity: 0.5; }
@media (max-width: 700px) {
body { padding: 12px; }
.header { padding: 24px 16px 20px; }
.header h1 { font-size: 18px; }
.card-body { padding: 16px; }
.card-header { padding: 12px 16px; }
.feature-list { grid-template-columns: 1fr; }
.input-row { flex-wrap: wrap; }
.input-row .prefix { width: 100%; justify-content: center; padding: 8px 12px; }
.domain-input { width: 100%; }
.url-input { flex: 1 1 100%; min-width: 100%; }
.sep { display: none; }
.btn-inline { flex: 1; justify-content: center; min-width: 0; }
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<div class="icon">🌐</div>
<h1>反向代理网关</h1>
<div class="badge">STATUS: RUNNING</div>
</div>
<div class="card">
<div class="card-header">
<span class="dot" style="background:var(--accent)"></span>
快速使用 / Quick Proxy
</div>
<div class="card-body">
<div class="mode-block">
<div class="mode-label">
<span class="icon">🔗</span> 模式一:标准通用代理
<span class="tag tag-standard">Standard</span>
</div>
<div class="mode-desc">
模拟浏览器请求代理目标资源,绕过 CORS 跨域限制。默认无伪装模式,如遇防盗链会自动切换伪装。支持所有文件类型。
</div>
<div class="input-row">
<span class="prefix">https://<span id="host1"></span>/</span>
<input type="text" class="url-input" id="url1"
placeholder="例如: https://example.com/video.mp4"
autocomplete="off" spellcheck="false">
<button class="btn-inline btn-visit" id="visit1" disabled>🔗 访问</button>
<button class="btn-inline btn-copy" id="copy1" disabled>📋 复制</button>
</div>
<div class="url-preview" id="preview1">
<span class="label">代理地址:</span>
<span class="full-url" id="previewUrl1"></span>
</div>
<div class="input-hint" id="hint1">输入完整的目标 URL(必须包含 http:// 或 https://)</div>
</div>
<div class="mode-block">
<div class="mode-label">
<span class="icon">🎯</span> 模式二:伪装域名代理
<span class="tag tag-disguise">Disguise</span>
</div>
<div class="mode-desc">
强制伪装 Referer 和 Origin 为指定域名,用于<strong style="color:var(--yellow)">绕过白名单防盗链</strong>(如 Pixiv、B站等)。
</div>
<div class="input-row">
<span class="prefix">https://<span id="host2"></span>/</span>
<input type="text" class="domain-input" id="domain2"
placeholder="伪装域名,例如: www.pixiv.net"
autocomplete="off" spellcheck="false">
<span class="sep">/</span>
<input type="text" class="url-input" id="url2"
placeholder="目标 URL,例如: https://example.com/image.jpg"
autocomplete="off" spellcheck="false">
<button class="btn-inline btn-visit" id="visit2" disabled>🔗 访问</button>
<button class="btn-inline btn-copy" id="copy2" disabled>📋 复制</button>
</div>
<div class="url-preview" id="preview2">
<span class="label">代理地址:</span>
<span class="full-url" id="previewUrl2"></span>
</div>
<div class="input-hint" id="hint2">输入伪装用的网站域名 + 完整的目标 URL</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<span class="dot" style="background:var(--green)"></span>
网关特性 / Features
</div>
<div class="card-body">
<ul class="feature-list">
<li><span class="check">✓</span><span>支持所有文件格式<br><small style="color:var(--text-secondary)">MP4 / MP3 / M3U8 / JPG / PNG / JS / CSS / HTML…</small></span></li>
<li><span class="check">✓</span><span>流媒体断点续传<br><small style="color:var(--text-secondary)">HTTP 206 Range,切片即播秒缓冲</small></span></li>
<li><span class="check">✓</span><span>客户端 UA 智能透传<br><small style="color:var(--text-secondary)">100% 真实浏览器标识,无特征泄漏</small></span></li>
<li><span class="check">✓</span><span>全局跨域 CORS 开放<br><small style="color:var(--text-secondary)">Access-Control-Allow-Origin: *</small></span></li>
<li><span class="check">✓</span><span>IP 链路高阶伪装<br><small style="color:var(--text-secondary)">多层 X-Forwarded-For 链路构造</small></span></li>
<li><span class="check">✓</span><span>智能降级重试<br><small style="color:var(--text-secondary)">防盗链自动切换到伪装模式</small></span></li>
</ul>
</div>
</div>
<div class="card">
<div class="card-header">
<span class="dot" style="background:var(--yellow)"></span>
调试响应头 / Debug Headers
</div>
<div class="card-body">
<ul class="tip-list">
<li><code style="font-size:12px;color:var(--accent)">X-Debug-Fake-Referer</code> — 当前使用的 Referer 伪装策略</li>
<li><code style="font-size:12px;color:var(--accent)">X-Debug-Upstream-IP</code> — 向上游传递的 IP 链路</li>
<li><code style="font-size:12px;color:var(--accent)">X-Debug-Upstream-UA</code> — 向上游传递的 User-Agent</li>
<li><code style="font-size:12px;color:var(--accent)">X-Debug-Proxy-Status</code> — 代理层最终响应状态码</li>
</ul>
</div>
</div>
<div class="card">
<div class="card-header">
<span class="dot" style="background:var(--cyan)"></span>
支持的方法 / Methods
</div>
<div class="card-body">
<div style="display:flex;gap:10px;flex-wrap:wrap;">
<span style="padding:4px 14px;background:var(--bg);border:1px solid var(--border);border-radius:4px;font-family:monospace;font-size:13px;color:var(--green)">GET</span>
<span style="padding:4px 14px;background:var(--bg);border:1px solid var(--border);border-radius:4px;font-family:monospace;font-size:13px;color:var(--green)">HEAD</span>
<span style="padding:4px 14px;background:var(--bg);border:1px solid var(--border);border-radius:4px;font-family:monospace;font-size:13px;color:var(--green)">OPTIONS</span>
<span style="padding:4px 14px;background:var(--bg);border:1px solid var(--border);border-radius:4px;font-family:monospace;font-size:13px;color:var(--green)">POST</span>
</div>
</div>
</div>
<div class="footer">Cloudflare Workers · 运行于 Cloudflare 全球边缘节点</div>
</div>
<script>
const host = window.location.host;
['host1', 'host2'].forEach(id => {
const el = document.getElementById(id);
if (el) el.textContent = host;
});
function isValidURL(str) {
if (!/^https?:\/\//i.test(str)) return false;
try { const u = new URL(str); return !!u.hostname && u.hostname.includes('.'); }
catch { return false; }
}
function isValidDomain(str) {
let domain = str.replace(/^https?:\/\//i, '').replace(/\/$/, '');
return /^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/.test(domain);
}
function setValidity(el, ok) { el.classList.remove('valid','invalid'); if (el.value.trim()) el.classList.add(ok?'valid':'invalid'); }
function clearValidity(el) { el.classList.remove('valid','invalid'); }
function updatePreview(previewBox, urlEl, visitBtn, copyBtn, proxyURL, ok) {
if (ok) {
urlEl.textContent = proxyURL; previewBox.classList.add('show');
visitBtn.disabled = false; copyBtn.disabled = false;
} else {
previewBox.classList.remove('show');
visitBtn.disabled = true; copyBtn.disabled = true;
}
}
function copyURL(url, btn) {
navigator.clipboard.writeText(url).then(() => {
btn.textContent = '✓'; btn.classList.add('copied');
setTimeout(() => { btn.textContent = '📋'; btn.classList.remove('copied'); }, 1500);
}).catch(() => {
btn.textContent = '✗';
setTimeout(() => { btn.textContent = '📋'; }, 1500);
});
}
// ── 模式一 ──
const url1 = document.getElementById('url1');
const hint1 = document.getElementById('hint1');
const prev1 = document.getElementById('preview1');
const prevUrl1 = document.getElementById('previewUrl1');
const visit1 = document.getElementById('visit1');
const copy1 = document.getElementById('copy1');
url1.addEventListener('input', () => {
const raw = url1.value.trim();
if (!raw) {
clearValidity(url1);
hint1.textContent = '输入完整的目标 URL(必须包含 http:// 或 https://)';
hint1.className = 'input-hint';
updatePreview(prev1, prevUrl1, visit1, copy1, '', false);
return;
}
if (isValidURL(raw)) {
setValidity(url1, true);
hint1.textContent = '✓ 格式正确';
hint1.className = 'input-hint success';
updatePreview(prev1, prevUrl1, visit1, copy1, 'https://'+host+'/'+raw, true);
} else {
setValidity(url1, false);
hint1.textContent = '⚠ URL 格式不正确,请确保包含完整协议(http:// 或 https://)且域名有效';
hint1.className = 'input-hint error';
updatePreview(prev1, prevUrl1, visit1, copy1, '', false);
}
});
visit1.addEventListener('click', () => { const u = prevUrl1.textContent; if (u) window.open(u, '_blank'); });
copy1.addEventListener('click', () => copyURL(prevUrl1.textContent, copy1));
url1.addEventListener('keydown', e => { if (e.key==='Enter' && !visit1.disabled) visit1.click(); });
// ── 模式二 ──
const domain2 = document.getElementById('domain2');
const url2 = document.getElementById('url2');
const hint2 = document.getElementById('hint2');
const prev2 = document.getElementById('preview2');
const prevUrl2 = document.getElementById('previewUrl2');
const visit2 = document.getElementById('visit2');
const copy2 = document.getElementById('copy2');
function checkMode2() {
const d = domain2.value.trim();
const u = url2.value.trim();
if (!d && !u) {
clearValidity(domain2); clearValidity(url2);
hint2.textContent = '输入伪装用的网站域名 + 完整的目标 URL';
hint2.className = 'input-hint';
updatePreview(prev2, prevUrl2, visit2, copy2, '', false);
return;
}
const dOk = d ? isValidDomain(d) : true;
const uOk = u ? isValidURL(u) : true;
const allOk = d && u && dOk && uOk;
if (d) setValidity(domain2, dOk); else clearValidity(domain2);
if (u) setValidity(url2, uOk); else clearValidity(url2);
if (allOk) {
hint2.textContent = '✓ 格式正确';
hint2.className = 'input-hint success';
const domain = d.replace(/^https?:\/\//i,'').replace(/\/$/,'');
updatePreview(prev2, prevUrl2, visit2, copy2, 'https://'+host+'/'+domain+'/'+u, true);
} else {
const msgs = [];
if (d && !dOk) msgs.push('伪装域名格式不正确');
if (u && !uOk) msgs.push('目标 URL 格式不正确');
hint2.textContent = msgs.length ? '⚠ '+msgs.join(';') : '⚠ 格式不正确';
hint2.className = 'input-hint error';
updatePreview(prev2, prevUrl2, visit2, copy2, '', false);
}
}
domain2.addEventListener('input', checkMode2);
url2.addEventListener('input', checkMode2);
visit2.addEventListener('click', () => { const u = prevUrl2.textContent; if (u) window.open(u, '_blank'); });
copy2.addEventListener('click', () => copyURL(prevUrl2.textContent, copy2));
url2.addEventListener('keydown', e => { if (e.key==='Enter' && !visit2.disabled) visit2.click(); });
domain2.addEventListener('keydown', e => { if (e.key==='Enter' && !visit2.disabled) visit2.click(); });
</script>
</body>
</html>