All checks were successful
Deploy production / deploy (push) Successful in 1m15s
- server.js wraps Bun.serve to add /ws/<channel> upgrades (same server and port) and serve the map tiles from the host mount; Dockerfile now runs bun ./server.js - ws hub (channel registry, origin check) + pubg-map channel: rooms by unguessable code, validated + rate-limited messages, per-room write queue, presence - games.pubg_rooms / pubg_matches / pubg_markers; per-match sessions with history - /trackers/games/pubg/map hub (owner: rooms, landings over time, history) and /trackers/games/pubg/map/<code> fullscreen Leaflet map (all 11 maps) - deploy-remote.sh bind-mounts ~/pubg-maps; scripts/build-pubg-tiles.py rebuilds it
75 lines
2.9 KiB
JavaScript
75 lines
2.9 KiB
JavaScript
/* global Bun */
|
|
// Production entry point: `bun ./server.js` instead of `bun ./build`. The adapter-bun build
|
|
// starts Bun.serve itself without WebSocket handlers, so this wraps Bun.serve before loading
|
|
// it and adds, on the same server and port:
|
|
// /ws/<channel> WebSocket upgrades, handled by #lib/server/ws/hub (registered in
|
|
// hooks.server.ts init, published on globalThis.__siteWs)
|
|
// /pubg-tiles/… the PUBG map tiles, straight from the bind-mounted PUBG_TILES_ROOT
|
|
// (immutable, so Cloudflare and browsers cache them; no session lookup)
|
|
// Everything else goes to SvelteKit unchanged.
|
|
import { resolve, sep } from 'node:path';
|
|
|
|
const TILE_RE = /^\/pubg-tiles\/([a-z-]{3,20})\/(?:(\d)\/(\d{1,2})\/(\d{1,2})|thumb)\.webp$/;
|
|
const tilesRoot = resolve(process.env.PUBG_TILES_ROOT || '/pubg-maps');
|
|
|
|
function clientIp(request, server) {
|
|
const h = request.headers;
|
|
return (
|
|
h.get('cf-connecting-ip') ||
|
|
h.get('x-forwarded-for')?.split(',')[0]?.trim() ||
|
|
server.requestIP(request)?.address ||
|
|
'unknown'
|
|
);
|
|
}
|
|
|
|
async function tile(pathname) {
|
|
const m = TILE_RE.exec(pathname);
|
|
if (!m) return new Response('Not Found', { status: 404 });
|
|
const rel = m[2] === undefined ? `${m[1]}/thumb.webp` : `${m[1]}/${m[2]}/${m[3]}/${m[4]}.webp`;
|
|
const path = resolve(tilesRoot, rel);
|
|
if (!path.startsWith(tilesRoot + sep)) return new Response('Not Found', { status: 404 });
|
|
const file = Bun.file(path);
|
|
if (!(await file.exists())) return new Response('Not Found', { status: 404 });
|
|
return new Response(file, {
|
|
headers: {
|
|
'content-type': 'image/webp',
|
|
'cache-control': 'public, max-age=31536000, immutable',
|
|
'access-control-allow-origin': '*'
|
|
}
|
|
});
|
|
}
|
|
|
|
const serve = Bun.serve.bind(Bun);
|
|
Bun.serve = (options) => {
|
|
const app = options.fetch;
|
|
options.fetch = async (request, server) => {
|
|
const { pathname } = new URL(request.url);
|
|
if (pathname.startsWith('/pubg-tiles/')) return tile(pathname);
|
|
if (pathname.startsWith('/ws/')) {
|
|
const hub = globalThis.__siteWs;
|
|
if (!hub) return new Response('Service Unavailable', { status: 503 });
|
|
if (request.headers.get('upgrade')?.toLowerCase() !== 'websocket')
|
|
return new Response('Upgrade Required', { status: 426 });
|
|
const data = await hub.authorize(request, new URL(request.url), clientIp(request, server));
|
|
if (data instanceof Response) return data;
|
|
if (server.upgrade(request, { data })) return undefined;
|
|
return new Response('Upgrade Required', { status: 426 });
|
|
}
|
|
return app(request, server);
|
|
};
|
|
options.websocket = {
|
|
// Messages are small JSON; Bun pings idle sockets so proxies keep them open.
|
|
maxPayloadLength: 16 * 1024,
|
|
idleTimeout: 60,
|
|
sendPings: true,
|
|
backpressureLimit: 1024 * 1024,
|
|
closeOnBackpressureLimit: true,
|
|
open: (ws) => globalThis.__siteWs?.open(ws),
|
|
message: (ws, message) => globalThis.__siteWs?.message(ws, message),
|
|
close: (ws) => globalThis.__siteWs?.close(ws)
|
|
};
|
|
return serve(options);
|
|
};
|
|
|
|
await import('./build/index.js');
|