① 登录 Cloudflare(cloudflare.com,免费注册),搜索并进入
Workers 和 Pages。
这个页面右下角"子域(subdomain)"就是你的专属域名,形如 你的名字.workers.dev——你的服务器地址就基于它。
② 建 Worker 并贴代码:Create → Worker,起名后 Deploy;再 Edit code,删掉默认代码,把下面整段粘进去,保存部署。
③ 挂一个数据库:Storage & Databases → KV → 新建一个命名空间;回到 Worker → Settings → Bindings → 加 KV 绑定,变量名必须填
LB,保存。
完成! 你的地址就是
https://worker名.你的子域.workers.dev。把它填进设置的“排行榜服务器地址”,再填昵称即可联机。
Worker 代码(点下方按钮复制):
// 浮岛群岛 · 多人排行榜 · Cloudflare Worker 版(含安全防护)
// 部署见《排行榜后端部署说明.md》。需要绑定一个 KV 命名空间,变量名设为 LB。
// 可选:在 Worker 的环境变量里设置 LB_SECRET,则提交分数需带 x-lb-key 请求头。
const MAX_INDEX = 5_000_000;
function cleanName(s) {
return String(s == null ? '玩家' : s).replace(/[<>&"'`\\/]/g, '').replace(/[\u0000-\u001f]/g, '').trim().slice(0, 16) || '玩家';
}
function cleanId(s) { return String(s == null ? '' : s).replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 40); }
function clampInt(v, max) { v = parseInt(v); if (!Number.isFinite(v) || v < 0) return 0; return Math.min(v, max); }
export default {
async fetch(request, env) {
const cors = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type,x-lb-key',
};
if (request.method === 'OPTIONS') return new Response(null, { status: 204, headers: cors });
const url = new URL(request.url);
// 简易限流:每 IP 10 秒最多 5 次(用 KV 计数)
const ip = request.headers.get('cf-connecting-ip') || 'anon';
const rk = 'rl:' + ip;
const cnt = parseInt(await env.LB.get(rk)) || 0;
if (cnt > 5) return new Response('{"error":"too many requests"}', { status: 429, headers: { 'Content-Type': 'application/json', ...cors } });
await env.LB.put(rk, String(cnt + 1), { expirationTtl: 10 });
// 取排行榜
if (request.method === 'GET' && url.pathname.startsWith('/leaderboard')) {
const raw = await env.LB.get('board');
const list = raw ? JSON.parse(raw) : [];
list.sort((a, b) => b.index - a.index);
return new Response(JSON.stringify(list.slice(0, 100)), { headers: { 'Content-Type': 'application/json', ...cors } });
}
// 提交分数
if (request.method === 'POST' && url.pathname.startsWith('/score')) {
if (env.LB_SECRET && request.headers.get('x-lb-key') !== env.LB_SECRET)
return new Response('{"error":"forbidden"}', { status: 403, headers: cors });
let d;
try { d = await request.json(); } catch (e) { return new Response('{"error":"bad json"}', { status: 400, headers: cors }); }
const id = cleanId(d.id);
const entry = {
id,
name: cleanName(d.name),
index: clampInt(d.index, MAX_INDEX),
maxWave: clampInt(d.maxWave, 100000),
occupied: clampInt(d.occupied, 5),
date: new Date().toISOString().slice(0, 10),
};
const raw = await env.LB.get('board');
let list = raw ? JSON.parse(raw) : [];
// 一个账号(id)只保留一条记录,且成绩只升不降
const prev = list.find(r => (id && r.id === id) || r.name === entry.name);
if (prev) { if (entry.index > prev.index) Object.assign(prev, entry); }
else list.push(entry);
list.sort((a, b) => b.index - a.index);
if (list.length > 500) list = list.slice(0, 500);
await env.LB.put('board', JSON.stringify(list));
return new Response(JSON.stringify({ ok: true }), { headers: { 'Content-Type': 'application/json', ...cors } });
}
return new Response('not found', { status: 404, headers: cors });
},
};
不想用 Cloudflare?随包还附了零依赖的 Node 版(leaderboard-server.js),可部署到 Railway/Render。安全防护(防注入/限流/数值钳制)均已内置。