// Isolated UI preview: synthetic data only. No upstream API or payment access. import { createServer } from "node:http"; import { readFile } from "node:fs/promises"; import { gzipSync } from "node:zlib"; import { randomBytes } from "node:crypto"; const port = Number(process.env.PREVIEW_PORT || 4173); const paused = process.env.PREVIEW_PAUSED === "true"; const states = new Map(); const attempts = new Map(); let folders = [ { id: "a".repeat(32), name: "本地预览 · 示例批次" }, { id: "b".repeat(32), name: "国庆活动" }, ]; const plaintext = new Map(); const now = Math.floor(Date.now() / 1000); let codes = ["active", "processing", "succeeded", "review", "revoked"].map( (status, index) => { // Two batches plus one unfiled code so client-side filtering is demonstrable. const folder = index < 2 ? folders[0] : index < 4 ? folders[1] : null; return { id: (index + 1).toString(16).padStart(32, "0"), folder: folder?.id ?? "", copyable: false, hint: `DEMO000${index}`, batch: folder?.name ?? "", months: index % 2 ? 3 : 6, status, username: index > 0 && index < 4 ? "demo_user" : "", message: status === "review" ? "示例:结果正在核实,请勿重复兑换。" : "", created: now - index * 3600, }; }, ); const files = { "/": ["index.html", "text/html"], "/admin": ["admin.html", "text/html"], "/appearance.js": ["appearance.js", "application/javascript"], "/app.js": ["app.js", "application/javascript"], "/admin.js": ["admin.js", "application/javascript"], "/favicon.svg": ["favicon.svg", "image/svg+xml"], }; createServer(async (req, res) => { const nonce = randomBytes(16).toString("hex"); res.setHeader("Cache-Control", "no-store"); res.setHeader( "Content-Security-Policy", `default-src 'none'; script-src 'self'; style-src 'self' 'nonce-${nonce}'; style-src-attr 'unsafe-inline'; connect-src 'self'; img-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'`, ); res.setHeader("X-Content-Type-Options", "nosniff"); const url = new URL(req.url, `http://127.0.0.1:${port}`); function json(status, data) { res.writeHead(status, { "Content-Type": "application/json" }); res.end(JSON.stringify(data)); } try { if (req.method === "GET" && files[url.pathname]) { const [file, type] = files[url.pathname]; let data = await readFile( new URL(`../../internal/site/assets/${file}`, import.meta.url), ); if (type === "text/html") data = data .toString() .replaceAll("__XGIFT_NONCE__", nonce) .replace("", " · 本地模拟预览"); res.setHeader("Vary", "Accept-Encoding"); if (/\bgzip\b/.test(req.headers["accept-encoding"] || "")) { data = gzipSync(data, { level: 9 }); res.setHeader("Content-Encoding", "gzip"); } res.writeHead(200, { "Content-Type": type }); res.end(data); return; } if (req.method === "GET" && url.pathname === "/healthz") { json(200, { ok: true, payments_enabled: !paused }); return; } if (req.method === "GET" && url.pathname === "/api/admin/codes") { const page = Math.max(0, Number(url.searchParams.get("page")) || 0); const folder = url.searchParams.get("folder") || ""; if ( folder && folder !== "unfiled" && !folders.some((item) => item.id === folder) ) { json(404, { message: "文件夹不存在,请刷新列表。" }); return; } const filtered = codes.filter( (code) => !folder || (folder === "unfiled" ? !code.folder : code.folder === folder), ); const stats = { total: codes.length, active: 0, processing: 0, succeeded: 0, review: 0, revoked: 0, unfiled: codes.filter((code) => !code.folder).length, }; for (const code of codes) stats[code.status]++; json(200, { codes: filtered.slice(page * 100, (page + 1) * 100), page, folder, stats, folders: folders .map((item) => ({ ...item, count: codes.filter((code) => code.folder === item.id).length, })) .sort((a, b) => a.name.localeCompare(b.name)), has_more: filtered.length > (page + 1) * 100, payments_enabled: !paused, }); return; } if (req.method !== "POST") { if (req.method === "GET" && url.pathname === "/api/admin/recovery") { json(200, { batch: null, network: { mode: "direct", nodes: 1 }, summary: { review: codes.filter((code) => code.status === "review").length, processing: codes.filter((code) => code.status === "processing") .length, }, }); return; } if (req.method === "GET" && url.pathname === "/api/admin/customer") { const key = url.searchParams.get("id") || ""; const user = (url.searchParams.get("username") || "") .replace(/^@/, "") .toLowerCase(); const found = codes.find( (code) => (key && code.id === key) || (user && code.username === user), ); if (!found) { json(404, { message: "没有找到对应的订单,请核对后重试。" }); return; } json(200, { order: { id: found.id, username: found.username, hint: found.hint, months: found.months, status: found.status, message: found.message, batch: found.batch, }, code: plaintext.get(found.id) || "", checkout_url: "", previous_checkout_url: "", can_recover: false, replacement_count: 0, }); return; } if (req.method === "GET" && url.pathname === "/api/admin/stats") { const daily = []; for (let offset = 29; offset >= 0; offset--) { const day = new Date(); day.setDate(day.getDate() - offset); const date = [ day.getFullYear(), String(day.getMonth() + 1).padStart(2, "0"), String(day.getDate()).padStart(2, "0"), ].join("-"); // Deterministic synthetic series with some zero days. const created = offset % 4 === 0 ? 0 : ((offset * 7) % 9) + 1; const redeemed = Math.max(0, created - (offset % 3)); const succeeded = Math.max(0, redeemed - (offset % 2)); daily.push({ date, created, redeemed, succeeded }); } json(200, { codes: { total: 240, active: 62, processing: 8, review: 3, succeeded: 150, revoked: 17, redeemed: 161, unfiled: 12, }, rates: { redeemed: 161 / 240, success: 150 / 161 }, months: [ { months: 3, total: 90, succeeded: 52 }, { months: 6, total: 150, succeeded: 98 }, ], daily, review_stages: [ { progress: 20, count: 1 }, { progress: 50, count: 1 }, { progress: 90, count: 1 }, ], }); return; } json(404, { message: "预览路由不存在" }); return; } let raw = ""; for await (const chunk of req) { raw += chunk; if (raw.length > 4096) { json(413, { message: "请求过大" }); return; } } const body = JSON.parse(raw); if (url.pathname.startsWith("/api/admin/recovery/")) { json(503, { message: "本地模拟预览不提供补单操作。" }); return; } if (url.pathname === "/api/admin/folders" || url.pathname === "/api/admin/folders/rename" ) { const name = typeof body.name === "string" ? body.name.trim() : ""; if (!name || Buffer.byteLength(name) > 120) { json(400, { message: "请输入有效文件夹名称(最多 120 字节)。" }); return; } if ( folders.some( (folder) => folder.name.toLowerCase() === name.toLowerCase() && folder.id !== body.id, ) ) { json(409, { message: "已存在同名文件夹。" }); return; } if (url.pathname.endsWith("/rename")) { const folder = folders.find((item) => item.id === body.id); if (!folder) { json(404, { message: "文件夹不存在。" }); return; } folder.name = name; for (const code of codes) if (code.folder === folder.id) code.batch = name; json(200, { message: "已重命名。" }); return; } const folder = { id: randomBytes(16).toString("hex"), name }; folders.push(folder); json(201, { ...folder, count: 0 }); return; } if (url.pathname === "/api/admin/folders/delete") { if (!folders.some((folder) => folder.id === body.id)) { json(404, { message: "文件夹不存在。" }); return; } folders = folders.filter((folder) => folder.id !== body.id); for (const code of codes) if (code.folder === body.id) { code.folder = ""; code.batch = ""; } json(200, { message: "文件夹已删除,兑换码已移至未分类。" }); return; } if (url.pathname === "/api/admin/codes/move") { if ( !Array.isArray(body.ids) || !body.ids.length || body.ids.length > 100 || new Set(body.ids).size !== body.ids.length || !body.ids.every((id) => codes.some((code) => code.id === id)) ) { json(409, { message: "兑换码选择无效,请刷新列表。" }); return; } if (body.folder && !folders.some((folder) => folder.id === body.folder)) { json(404, { message: "文件夹不存在。" }); return; } for (const code of codes) if (body.ids.includes(code.id)) { code.folder = body.folder || ""; code.batch = folders.find((f) => f.id === code.folder)?.name || ""; } json(200, { message: "已更新分类。" }); return; } if (url.pathname === "/api/admin/codes") { if ( ![3, 6].includes(body.months) || !Number.isInteger(body.count) || body.count < 1 || body.count > 500 || typeof body.batch !== "string" || Buffer.byteLength(body.batch) > 120 ) { json(400, { message: "请检查套餐、数量和批次名称。" }); return; } if (body.folder && !folders.some((folder) => folder.id === body.folder)) { json(404, { message: "文件夹不存在。" }); return; } let batch = body.batch || "本地预览-" + Date.now(); let folder = folders.find( (f) => f.name.toLowerCase() === batch.toLowerCase(), ); if (!folder) { folder = { id: randomBytes(16).toString("hex"), name: batch }; folders.push(folder); } batch = folder.name; body.folder = folder.id; const generated = Array.from( { length: body.count }, () => "XG-" + randomBytes(24).toString("hex").toUpperCase(), ); codes = [ ...generated.map((code) => { const id = randomBytes(16).toString("hex"); plaintext.set(id, code); return { id, copyable: true, folder: body.folder || "", hint: code.slice(-8), batch, months: body.months, status: "active", username: "", message: "", created: now, }; }), ...codes, ]; json(201, { codes: generated, batch, months: body.months, folder: body.folder || "", }); return; } if (url.pathname === "/api/admin/lookup") { const full = typeof body.code === "string" ? body.code.trim().toUpperCase() : ""; if (!/^XG-[A-F0-9]{48}$/.test(full)) { json(400, { message: "请填写 XG- 开头、后接 48 位字符的完整兑换码。" }); return; } // Fixed demo hit so a found result can be previewed without generating. if (full === "XG-" + "5".repeat(48)) { json(200, { ...codes[2], progress: 100, updated: now }); return; } const entry = [...plaintext.entries()].find( ([, value]) => value === full, ); const found = entry && codes.find((code) => code.id === entry[0]); json( found ? 200 : 404, found ? { ...found, updated: found.created, progress: 0 } : { message: "没有找到这个兑换码,请核对后重试。" }, ); return; } if (url.pathname === "/api/admin/codes/copy") { const code = plaintext.get(body.id); json( code ? 200 : 409, code ? { code } : { message: "历史兑换码未保存完整内容。" }, ); return; } if (url.pathname === "/api/admin/revoke") { const code = codes.find((item) => item.id === body.id); if (!code || code.status !== "active") { json(409, { message: "只能停用未使用的兑换码。" }); return; } code.status = "revoked"; json(200, { message: "兑换码已停用。" }); return; } if (url.pathname === "/api/check") { if (!/^[a-z0-9_]{1,15}$/.test(body.username || "")) { json(400, { message: "请填写正确的 X 用户名(不是显示名称)。" }); return; } if (body.username === "blocked_user") { json(200, { eligible: false, message: "示例:X 当前不允许向这个账号赠送 Premium。", }); return; } if (body.username === "busy_user") { json(503, { message: "暂时无法向 X 核实赠送资格,请稍后重试检测。" }); return; } json(200, { eligible: true, message: "该账号当前可以接收赠送。" }); return; } if (url.pathname === "/api/redeem" || url.pathname === "/api/status") { if ( !/^XG-[A-F0-9]{48}$/.test(body.code) || !/^[a-z0-9_]{1,15}$/.test(body.username) ) { json(400, { message: "请检查兑换码和用户名。" }); return; } const ending = body.code.slice(-1); if (ending === "C") { json(422, { message: "示例:X 当前不允许向这个账号赠送 Premium。兑换码未使用。", }); return; } if (ending === "D") { json(200, { status: "revoked", message: "兑换码已停用,请联系提供方。", }); return; } if (req.url === "/api/redeem" && paused) { json(503, { status: "paused", message: "账号可以接收赠送,但充值服务暂未开放。兑换码未使用。", }); return; } const key = body.code + ":" + body.username; if (req.url === "/api/redeem") { states.set(key, Date.now()); attempts.set(key, (attempts.get(key) || 0) + 1); } if (!states.has(key)) { json(200, { status: "active", progress: 0, months: 6 }); return; } const elapsed = Date.now() - states.get(key); if (ending === "E" && attempts.get(key) === 1 && elapsed > 3000) { json(200, { status: "review", progress: 50, months: 6, message: "本地模拟:原订单尚未付款,可重新检查并继续兑换。", }); return; } if (ending === "B" && elapsed > 3000) { json(200, { status: "review", progress: 90, months: 6, message: "示例:结果暂未确认,请查询原订单或联系管理员。", }); return; } const progress = ending === "F" ? 70 : Math.min(100, 20 + Math.floor(elapsed / 1500) * 20); json(200, { status: progress === 100 ? "succeeded" : "processing", progress, months: 6, message: progress === 100 ? `本地模拟:已为 @${body.username} 完成 6 个月 Premium 赠送(未执行真实付款)。` : "本地模拟:正在核验订单,请稍候。", }); return; } json(404, { message: "预览路由不存在" }); } catch { json(400, { message: "预览请求无法处理。" }); } }).listen(port, "127.0.0.1", () => console.log( `本地模拟预览:http://127.0.0.1:${port} /admin;所有数据均为示例,不连接生产或付款服务。`, ), );