import { Elysia, t } from 'elysia'; import { html } from '@elysiajs/html'; import { staticPlugin } from '@elysiajs/static'; import { cookie } from '@elysiajs/cookie'; import { WebSocketHandler } from './web-socket-handler'; import { ElysiaWS } from 'elysia/dist/ws'; import { createStoneSvg } from './view/board-renderer'; const wsHandler = new WebSocketHandler(); export type WS = ElysiaWS<{ query: { playerId: string; gameId: string } }>; const app = new Elysia() .use( staticPlugin({ assets: './public', prefix: '/', }), ) .use(cookie()) .use(html()) .ws('/ws', { query: t.Object({ gameId: t.String(), playerId: t.String(), }), open(ws) { wsHandler.handleConnection(ws); }, message(ws, message) { wsHandler.handleMessage(ws, message); }, close(ws) { wsHandler.handleDisconnect(ws); }, }) .get('/', async ({ query, cookie, request: _request }) => { let playerId: string; const existingPlayerId = cookie.playerId?.value; if (existingPlayerId) { playerId = existingPlayerId; console.log(`Using existing playerId from cookie: ${playerId}`); } else { playerId = `player-${Math.random().toString(36).substring(2, 9)}`; cookie.playerId.set({ value: playerId, httpOnly: true, path: '/', maxAge: 30 * 24 * 60 * 60, }); console.log(`Generated new playerId and set cookie: ${playerId}`); } let gameId = query.gameId as string | undefined; if (gameId) { if (!wsHandler.hasGame(gameId)) { wsHandler.createGame(gameId); console.log(`Created new game with provided ID: ${gameId}`); } else { console.log(`Player ${playerId} visited existing game ${gameId}`); } } else { gameId = wsHandler.createGame(); console.log( `Player ${playerId} came without a gameId. Redirecting them to new game ${gameId}.`, ); return new Response(null, { status: 302, headers: { Location: `/?gameId=${gameId}` }, }); } const displayName = wsHandler.getPlayerName(playerId); const htmlTemplate = await Bun.file('./public/index.html').text(); let finalHtml = htmlTemplate .replace( '', ``, ) .replace( '', ``, ) .replace( '', ``, ); return new Response(finalHtml, { headers: { 'Content-Type': 'text/html' }, status: 200, }); }) .get('/black-stone.svg', () => { const stoneSvg = createStoneSvg('black', false); return new Response(String(stoneSvg), { headers: { 'Content-Type': 'image/svg+xml' }, status: 200, }); }) .get('/white-stone.svg', () => { const stoneSvg = createStoneSvg('white', false); return new Response(String(stoneSvg), { headers: { 'Content-Type': 'image/svg+xml' }, status: 200, }); }); const port = Number(process.env.PORT || 3000); app.listen(port, () => { console.log(`🦊 Elysia is running at ${app.server?.hostname}:${port}`); });