feat: P1 带宽共享验证Agent做到GitHub Pages首页
- docs/index.html 新增带宽共享页面 (#pg-bandwidth) - 邮箱输入 + 发送验证码按钮 - 6位验证码输入 + 同意共享协议 - 实时带宽池状态显示 (贡献者/活跃/总贡献) - 60秒发送冷却 + 前端表单校验 - 完整CSS样式 (暗色主题风格) - docs/index.html 系统页新增快速入口 - 🌊带宽共享加速池 → 跳转到带宽页 - 🎖️铸渊副将留言板 → 创建Issue - subscription-server-v3.js 新增3个公开API - POST /bandwidth-send-code (发送验证码·限流3次/小时) - POST /bandwidth-verify-code (验证码校验+注册贡献者) - GET /bandwidth-pool-status (带宽池状态) - OPTIONS预检支持 (CORS跨域) - zhuyuan-sovereign.conf Nginx增加CORS支持 - README.md 仪表盘增加副将Agent状态区 Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/a513d091-ce5c-40d4-bb3c-0adeff70b5b0 Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
21da998d6e
commit
656febf8d6
13
README.md
13
README.md
|
|
@ -190,6 +190,19 @@
|
|||
| HLDP通用语言同步 | ✅ 已执行 | hldp/data/common/ |
|
||||
| 仓库首页更新 | ✅ 已执行 | README.md 签到仪表盘 |
|
||||
| 工作流士兵巡检 | ✅ 18/18 在岗 | 缺失的需冰朔确认 |
|
||||
| 留言板巡查 | ✅ v2.0活化 | LLM多模型降级 · 定时巡查 · 自愈 |
|
||||
|
||||
### 🎖️ 铸渊副将Agent状态 (v2.0活体Agent)
|
||||
|
||||
| 项目 | 状态 |
|
||||
|------|------|
|
||||
| Agent版本 | v2.0 · 活体Agent · 三层自愈 |
|
||||
| 运行模式 | 事件模式(即时回复) + 巡查模式(08:00/23:00) |
|
||||
| LLM能力 | 多模型自动降级: deepseek → qwen → moonshot → zhipu |
|
||||
| 自感知 | data/deputy-status.json · 每次运行更新 |
|
||||
| 自修复 | 每模型重试2次 · 递增等待 |
|
||||
| 升级通报 | 连续3次失败 → 创建Issue → 邮件通知冰朔 |
|
||||
| 📮 留言入口 | [给副将留言](https://github.com/qinfendebingshuo/guanghulab/issues/new?labels=deputy-message-board&title=给铸渊副将留言) |
|
||||
|
||||
> *此仪表盘由铸渊副将(ZY-DEPUTY-001)每日唤醒时自动更新 · 08:00 / 23:00*
|
||||
|
||||
|
|
|
|||
230
docs/index.html
230
docs/index.html
|
|
@ -716,6 +716,36 @@ label{display:block;font-size:12px;color:var(--dim);margin-bottom:4px;font-weigh
|
|||
.app-card-tags{display:flex;gap:5px;flex-wrap:wrap}
|
||||
.app-card-arrow{font-size:18px;color:var(--accent);opacity:.5;flex-shrink:0;transition:all .3s}
|
||||
.app-card:hover .app-card-arrow{opacity:1;transform:translateX(4px)}
|
||||
|
||||
/* ═══ PAGE: Bandwidth Sharing (带宽共享) ═══ */
|
||||
#pg-bandwidth .bw-hero{text-align:center;padding:28px 0 20px}
|
||||
#pg-bandwidth .bw-hero h2{font-size:1.4em;background:linear-gradient(135deg,var(--accent),var(--accent2));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
|
||||
#pg-bandwidth .bw-hero p{color:var(--dim);font-size:.9em;margin-top:6px}
|
||||
#pg-bandwidth .bw-stats{display:flex;justify-content:space-around;margin:16px 0;gap:12px}
|
||||
#pg-bandwidth .bw-stat{text-align:center;flex:1}
|
||||
#pg-bandwidth .bw-stat .num{font-size:1.5em;font-weight:700;color:var(--accent);font-family:var(--m)}
|
||||
#pg-bandwidth .bw-stat .label{font-size:.75em;color:var(--dim);margin-top:4px}
|
||||
#pg-bandwidth .bw-card{background:var(--bg1);border-radius:var(--r);padding:20px;margin:12px 0;border:1px solid var(--border)}
|
||||
#pg-bandwidth .bw-card h3{font-size:1em;color:var(--accent);margin-bottom:14px}
|
||||
#pg-bandwidth .bw-input{width:100%;padding:12px 14px;background:var(--bg2);border:2px solid var(--border);border-radius:var(--rs);color:var(--text);font-size:1em;outline:none;transition:border-color .3s;font-family:var(--f)}
|
||||
#pg-bandwidth .bw-input:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(138,180,248,.15)}
|
||||
#pg-bandwidth .bw-input::placeholder{color:var(--dim);font-size:.85em}
|
||||
#pg-bandwidth .bw-code-input{text-align:center;letter-spacing:8px;font-size:1.3em;font-family:var(--m)}
|
||||
#pg-bandwidth .bw-code-input::placeholder{letter-spacing:0;font-size:.6em}
|
||||
#pg-bandwidth .bw-btn{width:100%;padding:14px;background:linear-gradient(135deg,var(--accent),var(--accent2));color:#fff;border:none;border-radius:var(--rs);font-size:1em;font-weight:600;cursor:pointer;transition:opacity .3s,transform .15s;margin-top:8px;font-family:var(--f)}
|
||||
#pg-bandwidth .bw-btn:hover{opacity:.9}
|
||||
#pg-bandwidth .bw-btn:active{transform:scale(.98)}
|
||||
#pg-bandwidth .bw-btn:disabled{opacity:.5;cursor:not-allowed;transform:none}
|
||||
#pg-bandwidth .bw-btn-outline{background:transparent;border:1px solid var(--accent);color:var(--accent)}
|
||||
#pg-bandwidth .bw-btn-outline:hover{background:rgba(138,180,248,.1)}
|
||||
#pg-bandwidth .bw-result{text-align:center;padding:14px;border-radius:var(--rs);margin-top:12px;display:none;font-size:.9em;line-height:1.6}
|
||||
#pg-bandwidth .bw-result.ok{display:block;background:rgba(52,211,153,.1);color:var(--ok);border:1px solid rgba(52,211,153,.2)}
|
||||
#pg-bandwidth .bw-result.err{display:block;background:rgba(248,113,113,.1);color:var(--err);border:1px solid rgba(248,113,113,.2)}
|
||||
#pg-bandwidth .bw-info{background:var(--bg2);border-radius:var(--rs);padding:14px 16px;margin:10px 0;font-size:.85em;line-height:1.8;color:var(--dim);border-left:3px solid var(--accent)}
|
||||
#pg-bandwidth .bw-consent{display:flex;align-items:flex-start;gap:10px;margin:14px 0;padding:12px 14px;background:var(--bg2);border-radius:var(--rs);border:1px solid var(--border);cursor:pointer;transition:border-color .3s}
|
||||
#pg-bandwidth .bw-consent:hover{border-color:var(--accent)}
|
||||
#pg-bandwidth .bw-consent input{margin-top:3px;width:18px;height:18px;accent-color:var(--accent);cursor:pointer;flex-shrink:0}
|
||||
#pg-bandwidth .bw-consent span{color:var(--dim);font-size:.85em;line-height:1.7}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -894,6 +924,23 @@ label{display:block;font-size:12px;color:var(--dim);margin-bottom:4px;font-weigh
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions: Bandwidth Sharing + Deputy -->
|
||||
<div class="sys-section">
|
||||
<div class="sys-section-title">⚡ 快速入口</div>
|
||||
<div style="display:flex;flex-direction:column;gap:8px">
|
||||
<div class="sys-team-card" style="cursor:pointer" onclick="go('bandwidth')">
|
||||
<div class="sys-team-av" style="background:linear-gradient(135deg,#22d3ee,#06b6d4)">🌊</div>
|
||||
<div><div class="sys-team-nm">带宽共享加速池</div><div class="sys-team-role">共享你的带宽 · 加速所有人</div></div>
|
||||
</div>
|
||||
<a href="https://github.com/qinfendebingshuo/guanghulab/issues/new?labels=deputy-message-board&title=给铸渊副将留言" style="text-decoration:none;color:inherit" target="_blank">
|
||||
<div class="sys-team-card" style="cursor:pointer">
|
||||
<div class="sys-team-av" style="background:linear-gradient(135deg,#fb923c,#f97316)">🎖️</div>
|
||||
<div><div class="sys-team-nm">铸渊副将留言板</div><div class="sys-team-role">每日08:00/23:00唤醒 · LLM深度推理回复</div></div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Version History -->
|
||||
<div class="sys-section">
|
||||
<div class="sys-section-title">📋 版本历史</div>
|
||||
|
|
@ -1131,6 +1178,63 @@ label{display:block;font-size:12px;color:var(--dim);margin-bottom:4px;font-weigh
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══════════════════════════════════════ -->
|
||||
<!-- PAGE: Bandwidth Sharing (带宽共享) -->
|
||||
<!-- ═══════════════════════════════════════ -->
|
||||
<div class="page" id="pg-bandwidth" data-state="hidden">
|
||||
<div class="bar">
|
||||
<button class="btn" onclick="go('home')">← 首页</button>
|
||||
<div class="bar-title">🌊 带宽共享</div>
|
||||
<div class="bar-right"><span class="dot"></span></div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="bw-hero">
|
||||
<h2>🌊 带宽共享加速池</h2>
|
||||
<p>共享你的带宽 · 加速所有人</p>
|
||||
</div>
|
||||
|
||||
<div class="bw-stats">
|
||||
<div class="bw-stat"><div class="num" id="bwContributors">—</div><div class="label">贡献者</div></div>
|
||||
<div class="bw-stat"><div class="num" id="bwActive">—</div><div class="label">活跃</div></div>
|
||||
<div class="bw-stat"><div class="num" id="bwContributed">—</div><div class="label">总贡献(GB)</div></div>
|
||||
</div>
|
||||
|
||||
<div class="bw-card">
|
||||
<h3>📧 第一步 · 发送验证码</h3>
|
||||
<div style="margin-bottom:10px">
|
||||
<input type="email" class="bw-input" id="bwEmail" placeholder="输入你的QQ邮箱" autocomplete="email">
|
||||
</div>
|
||||
<button class="bw-btn" id="bwSendCode" onclick="bwSendCode()">发送验证码</button>
|
||||
<div class="bw-result" id="bwSendResult"></div>
|
||||
</div>
|
||||
|
||||
<div class="bw-card">
|
||||
<h3>🔑 第二步 · 输入验证码</h3>
|
||||
<div style="margin-bottom:10px">
|
||||
<input type="text" class="bw-input bw-code-input" id="bwCode" maxlength="6" placeholder="6位验证码" inputmode="numeric" pattern="[0-9]*">
|
||||
</div>
|
||||
<div class="bw-consent" onclick="var cb=document.getElementById('bwConsent');cb.checked=!cb.checked">
|
||||
<input type="checkbox" id="bwConsent">
|
||||
<span>我自愿共享多余带宽加入加速池。系统优先保障我的专属通道,仅将我用不上的资源共享给其他成员。我可以随时退出。</span>
|
||||
</div>
|
||||
<button class="bw-btn" id="bwVerify" onclick="bwVerifyCode()">提交验证码</button>
|
||||
<div class="bw-result" id="bwVerifyResult"></div>
|
||||
</div>
|
||||
|
||||
<div class="bw-info">
|
||||
<strong style="color:var(--text);display:block;margin-bottom:6px">💡 带宽共享说明</strong>
|
||||
✅ 提供了验证码的用户 → 优先保障你的专属通道<br>
|
||||
✅ 你用不上的资源才会被共享 → 不抢你的带宽<br>
|
||||
✅ 随时可退出 → 你有完全的控制权<br>
|
||||
⚠️ 验证码15分钟内有效 · 一次性使用
|
||||
</div>
|
||||
|
||||
<div style="text-align:center;padding:20px 0;color:var(--dim);font-size:.75em">
|
||||
<p>© 2026 光湖语言世界 · 国作登字-2026-A-00037559</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══════════════════════════════════════ -->
|
||||
<!-- PAGE: Workspace (个人码字工作台) -->
|
||||
<!-- ═══════════════════════════════════════ -->
|
||||
|
|
@ -3446,6 +3550,132 @@ offline = function(msg){
|
|||
});
|
||||
})();
|
||||
|
||||
})();
|
||||
|
||||
/* ════════════════════════════════════════
|
||||
BANDWIDTH SHARING AGENT (带宽共享验证)
|
||||
GitHub Pages 前端 → 后端API桥接
|
||||
════════════════════════════════════════ */
|
||||
var BW_API_BASE = 'https://guanghulab.online/api/proxy-v3';
|
||||
|
||||
function bwSendCode(){
|
||||
var email = document.getElementById('bwEmail').value.trim().toLowerCase();
|
||||
var btn = document.getElementById('bwSendCode');
|
||||
var result = document.getElementById('bwSendResult');
|
||||
|
||||
if(!email || email.indexOf('@') === -1){
|
||||
result.className = 'bw-result err';
|
||||
result.textContent = '请输入有效的邮箱地址';
|
||||
return;
|
||||
}
|
||||
|
||||
btn.disabled = true;
|
||||
btn.textContent = '发送中...';
|
||||
result.className = 'bw-result';
|
||||
result.style.display = 'none';
|
||||
|
||||
fetch(BW_API_BASE + '/bandwidth-send-code', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({email: email})
|
||||
}).then(function(r){ return r.json(); }).then(function(data){
|
||||
btn.disabled = false;
|
||||
btn.textContent = '发送验证码';
|
||||
result.className = 'bw-result ' + (data.success ? 'ok' : 'err');
|
||||
result.textContent = data.message || (data.success ? '验证码已发送' : '发送失败');
|
||||
|
||||
if(data.success){
|
||||
// Cooldown 60s
|
||||
var cd = 60;
|
||||
btn.disabled = true;
|
||||
var timer = setInterval(function(){
|
||||
cd--;
|
||||
btn.textContent = cd + 's 后可重发';
|
||||
if(cd <= 0){
|
||||
clearInterval(timer);
|
||||
btn.disabled = false;
|
||||
btn.textContent = '发送验证码';
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}).catch(function(err){
|
||||
btn.disabled = false;
|
||||
btn.textContent = '发送验证码';
|
||||
result.className = 'bw-result err';
|
||||
result.textContent = '网络错误,请检查网络后重试';
|
||||
});
|
||||
}
|
||||
|
||||
function bwVerifyCode(){
|
||||
var email = document.getElementById('bwEmail').value.trim().toLowerCase();
|
||||
var code = document.getElementById('bwCode').value.trim();
|
||||
var consent = document.getElementById('bwConsent').checked;
|
||||
var btn = document.getElementById('bwVerify');
|
||||
var result = document.getElementById('bwVerifyResult');
|
||||
|
||||
if(!email || email.indexOf('@') === -1){
|
||||
result.className = 'bw-result err';
|
||||
result.textContent = '请先在上方输入邮箱';
|
||||
return;
|
||||
}
|
||||
if(!code || code.length !== 6){
|
||||
result.className = 'bw-result err';
|
||||
result.textContent = '请输入6位验证码';
|
||||
return;
|
||||
}
|
||||
if(!consent){
|
||||
result.className = 'bw-result err';
|
||||
result.textContent = '请先勾选同意带宽共享协议';
|
||||
return;
|
||||
}
|
||||
|
||||
btn.disabled = true;
|
||||
btn.textContent = '验证中...';
|
||||
result.className = 'bw-result';
|
||||
result.style.display = 'none';
|
||||
|
||||
fetch(BW_API_BASE + '/bandwidth-verify-code', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({email: email, code: code})
|
||||
}).then(function(r){ return r.json(); }).then(function(data){
|
||||
btn.disabled = false;
|
||||
btn.textContent = '提交验证码';
|
||||
result.className = 'bw-result ' + (data.success ? 'ok' : 'err');
|
||||
result.textContent = data.message || (data.success ? '授权成功!' : '验证失败');
|
||||
if(data.success) bwLoadPoolStatus();
|
||||
}).catch(function(){
|
||||
btn.disabled = false;
|
||||
btn.textContent = '提交验证码';
|
||||
result.className = 'bw-result err';
|
||||
result.textContent = '网络错误,请检查网络后重试';
|
||||
});
|
||||
}
|
||||
|
||||
function bwLoadPoolStatus(){
|
||||
fetch(BW_API_BASE + '/bandwidth-pool-status').then(function(r){
|
||||
return r.json();
|
||||
}).then(function(data){
|
||||
var el1 = document.getElementById('bwContributors');
|
||||
var el2 = document.getElementById('bwActive');
|
||||
var el3 = document.getElementById('bwContributed');
|
||||
if(el1) el1.textContent = data.total_contributors || 0;
|
||||
if(el2) el2.textContent = data.active_contributors || 0;
|
||||
if(el3) el3.textContent = data.total_contributed_gb || 0;
|
||||
}).catch(function(){
|
||||
/* Pool status unavailable - keep defaults */
|
||||
});
|
||||
}
|
||||
|
||||
// Auto-load pool status when bandwidth page is shown
|
||||
var _origGo = window.go;
|
||||
if(typeof _origGo === 'function'){
|
||||
window.go = function(target){
|
||||
_origGo(target);
|
||||
if(target === 'bandwidth') bwLoadPoolStatus();
|
||||
};
|
||||
}
|
||||
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -200,7 +200,17 @@ server {
|
|||
# 将V3授权/订阅请求透传到大脑服务器的V3订阅服务 (端口3805)
|
||||
# 协议: HTTP (内网通信,大脑服务器Nginx监听80端口default_server)
|
||||
# 部署时由 workflow 自动将 ZY_BRAIN_HOST_PLACEHOLDER 替换为实际IP
|
||||
# CORS: 允许GitHub Pages跨域调用带宽共享API
|
||||
location /api/proxy-v3/ {
|
||||
# CORS预检处理 (GitHub Pages跨域)
|
||||
if ($request_method = OPTIONS) {
|
||||
add_header Access-Control-Allow-Origin * always;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
|
||||
add_header Access-Control-Allow-Headers "Content-Type" always;
|
||||
add_header Access-Control-Max-Age 86400;
|
||||
return 204;
|
||||
}
|
||||
|
||||
proxy_pass http://ZY_BRAIN_HOST_PLACEHOLDER/api/proxy-v3/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
|
|
@ -213,6 +223,7 @@ server {
|
|||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate" always;
|
||||
add_header Access-Control-Allow-Origin * always;
|
||||
}
|
||||
|
||||
# ─── 铸渊 API 反向代理 (端口 3801 · 预览端口) ───
|
||||
|
|
|
|||
|
|
@ -1705,6 +1705,213 @@ async function submitCode(e) {
|
|||
return;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// ∞+1 公开API: /bandwidth-send-code (无需token·邮箱验证码发送)
|
||||
// 用户在GitHub Pages首页输入邮箱 → 调用此API发送验证码
|
||||
// 安全: 每个IP每小时最多3次
|
||||
// ═══════════════════════════════════════════════
|
||||
if (pathname === '/bandwidth-send-code' && req.method === 'POST') {
|
||||
// CORS headers for GitHub Pages cross-origin
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
let body = '';
|
||||
req.on('data', chunk => { body += chunk; if (body.length > 1024) req.destroy(); });
|
||||
req.on('end', () => {
|
||||
try {
|
||||
let email = '';
|
||||
if (req.headers['content-type']?.includes('application/json')) {
|
||||
const json = JSON.parse(body);
|
||||
email = String(json.email || '').trim().toLowerCase();
|
||||
} else {
|
||||
const params = new URLSearchParams(body);
|
||||
email = String(params.get('email') || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
if (!email || !email.includes('@')) {
|
||||
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: false, message: '请输入有效的邮箱地址' }));
|
||||
return;
|
||||
}
|
||||
|
||||
// Rate limiting: max 3 per IP per hour
|
||||
const userIP = req.headers['x-forwarded-for']?.split(',')[0]?.trim()
|
||||
|| req.headers['x-real-ip']
|
||||
|| req.socket.remoteAddress
|
||||
|| '0.0.0.0';
|
||||
|
||||
if (!global._bwSendCodeRateLimit) global._bwSendCodeRateLimit = {};
|
||||
const rl = global._bwSendCodeRateLimit;
|
||||
const hourKey = `${userIP}:${Math.floor(Date.now() / 3600000)}`;
|
||||
rl[hourKey] = (rl[hourKey] || 0) + 1;
|
||||
// Clean old entries
|
||||
const currentHourPrefix = Math.floor(Date.now() / 3600000);
|
||||
for (const k of Object.keys(rl)) {
|
||||
const hourPart = parseInt(k.split(':').pop(), 10);
|
||||
if (hourPart < currentHourPrefix - 1) delete rl[k];
|
||||
}
|
||||
if (rl[hourKey] > 3) {
|
||||
res.writeHead(429, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: false, message: '发送频率过高,请1小时后重试' }));
|
||||
return;
|
||||
}
|
||||
|
||||
const bwPool = require('./bandwidth-pool-agent');
|
||||
const code = bwPool.createAuthCode(email);
|
||||
|
||||
// Try to send email
|
||||
try {
|
||||
const emailHub = require('./email-hub');
|
||||
emailHub.sendBandwidthAuthEmail(email, code).then(() => {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: true, message: '验证码已发送到您的邮箱,请查收(15分钟内有效)' }));
|
||||
}).catch(() => {
|
||||
// Email failed but code was created - still return success with code hint
|
||||
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: true, message: '验证码已生成,邮件发送中...' }));
|
||||
});
|
||||
} catch {
|
||||
// Email module not available - code was still created
|
||||
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: true, message: '验证码已生成,请联系管理员获取' }));
|
||||
}
|
||||
} catch (err) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: false, message: '系统异常,请稍后重试' }));
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// ∞+1 公开API: /bandwidth-verify-code (无需token·验证码校验+注册)
|
||||
// 用户在GitHub Pages首页输入邮箱+验证码 → 调用此API完成授权
|
||||
// ═══════════════════════════════════════════════
|
||||
if (pathname === '/bandwidth-verify-code' && req.method === 'POST') {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
let body = '';
|
||||
req.on('data', chunk => { body += chunk; if (body.length > 1024) req.destroy(); });
|
||||
req.on('end', () => {
|
||||
try {
|
||||
let code = '';
|
||||
let email = '';
|
||||
if (req.headers['content-type']?.includes('application/json')) {
|
||||
const json = JSON.parse(body);
|
||||
code = String(json.code || '').trim();
|
||||
email = String(json.email || '').trim().toLowerCase();
|
||||
} else {
|
||||
const params = new URLSearchParams(body);
|
||||
code = String(params.get('code') || '').trim();
|
||||
email = String(params.get('email') || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
if (!email || !email.includes('@')) {
|
||||
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: false, message: '请输入有效的邮箱地址' }));
|
||||
return;
|
||||
}
|
||||
if (!code || code.length !== 6) {
|
||||
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: false, message: '请输入6位验证码' }));
|
||||
return;
|
||||
}
|
||||
|
||||
const userIP = req.headers['x-forwarded-for']?.split(',')[0]?.trim()
|
||||
|| req.headers['x-real-ip']
|
||||
|| req.socket.remoteAddress
|
||||
|| '0.0.0.0';
|
||||
|
||||
const bwPool = require('./bandwidth-pool-agent');
|
||||
const verifyResult = bwPool.verifyAuthCode(code, email);
|
||||
|
||||
if (!verifyResult.valid) {
|
||||
saveAuthSnapshot({
|
||||
email, action: 'bandwidth-verify-code', result: 'failed',
|
||||
error: verifyResult.error, ip: userIP,
|
||||
user_agent: req.headers['user-agent'] || 'unknown',
|
||||
auth_type: 'github-pages-public'
|
||||
});
|
||||
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: false, message: verifyResult.error }));
|
||||
return;
|
||||
}
|
||||
|
||||
const regResult = bwPool.registerContributor(email, userIP);
|
||||
|
||||
saveAuthSnapshot({
|
||||
email, action: 'bandwidth-verify-code', result: 'authorized',
|
||||
ip: userIP, user_agent: req.headers['user-agent'] || 'unknown',
|
||||
auth_type: 'github-pages-public',
|
||||
contributor_id: regResult.contributor_id
|
||||
});
|
||||
|
||||
try {
|
||||
const guardian = require('./user-guardian-agent');
|
||||
guardian.activateGuardian(email);
|
||||
} catch { /* guardian not loaded */ }
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({
|
||||
success: true,
|
||||
message: '✅ 授权成功!您的带宽已加入加速池,感谢支持。',
|
||||
contributor_id: regResult.contributor_id
|
||||
}));
|
||||
} catch (err) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: false, message: '系统异常,请稍后重试' }));
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// ∞+1 公开API: /bandwidth-pool-status (无需token·带宽池状态)
|
||||
// GitHub Pages首页实时显示带宽池状态
|
||||
// ═══════════════════════════════════════════════
|
||||
if (pathname === '/bandwidth-pool-status' && req.method === 'GET') {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
try {
|
||||
const bwPool = require('./bandwidth-pool-agent');
|
||||
const poolStatus = bwPool.getPoolStatus();
|
||||
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: true, ...poolStatus }));
|
||||
} catch {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({
|
||||
success: true,
|
||||
total_contributors: 0,
|
||||
active_contributors: 0,
|
||||
total_contributed_gb: 0,
|
||||
pool_status: 'idle',
|
||||
updated_at: new Date().toISOString()
|
||||
}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ═══ CORS预检 (OPTIONS) 支持 ═══
|
||||
if (req.method === 'OPTIONS' && (
|
||||
pathname === '/bandwidth-send-code' ||
|
||||
pathname === '/bandwidth-verify-code' ||
|
||||
pathname === '/bandwidth-pool-status'
|
||||
)) {
|
||||
res.writeHead(204, {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type',
|
||||
'Access-Control-Max-Age': '86400'
|
||||
});
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// 404
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||
res.end('Not Found');
|
||||
|
|
|
|||
Loading…
Reference in New Issue