Files
blog/blog-admin/tools/human-demo-server.mjs
T
zqlit a74b3c7127
Deploy to Production / pre-check (push) Successful in 58s
Deploy to Production / build (push) Successful in 4m3s
Deploy to Production / deploy-edgeone (push) Successful in 3m48s
Deploy to Production / finalize (push) Successful in 26s
Deploy to Production / notify-failure (push) Skipped
归档 artalk-cf 评论后端 + rss-robot 到 blog-admin(含技术选型/模块分布 README)
2026-10-04 08:45:40 +08:00

47 lines
1.8 KiB
JavaScript

// 人机验证预览页的本地服务:静态托管 demo + 把 /api/* 同源转发到线上 Worker。
// 为什么要转发:Worker 的 CORS 白名单只放行自己的域名,file:// 或本地预览源的
// 跨域请求会被浏览器拦掉;同源就不存在这个问题,预览也能跑真实链路。
import { readFileSync } from 'node:fs';
import { createServer } from 'node:http';
const PORT = Number(process.argv[2] || 8791);
const FILE = process.argv[3] || 'C:/Users/QunLIn/WorkBuddy/2026-10-02-15-13-24/human-check-demo.html';
const UPSTREAM = 'https://api.200181.xyz';
const server = createServer(async (req, res) => {
if (req.url.startsWith('/api/')) {
const chunks = [];
for await (const c of req) chunks.push(c);
const body = Buffer.concat(chunks);
try {
const up = await fetch(UPSTREAM + req.url, {
method: req.method,
headers: {
'Content-Type': req.headers['content-type'] || 'application/json',
'User-Agent': 'human-demo-proxy/1.0',
},
body: req.method === 'GET' || req.method === 'HEAD' ? undefined : body,
});
const buf = Buffer.from(await up.arrayBuffer());
res.writeHead(up.status, { 'Content-Type': up.headers.get('content-type') || 'application/json' });
res.end(buf);
} catch (e) {
res.writeHead(502, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'upstream failed: ' + e.message }));
}
return;
}
try {
const html = readFileSync(FILE);
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(html);
} catch (e) {
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('demo file not found: ' + FILE);
}
});
server.listen(PORT, '127.0.0.1', () => {
console.log('人机验证预览: http://127.0.0.1:' + PORT + '/');
});