import indexHTML from "./pages/index.html"; import { createSession, createUser, deleteSession, getUserBySession, getUserByUsername, getSessionFromRequest, } from "./lib/auth"; import { createAuthenticationOptions, createRegistrationOptions, deletePasskey, getPasskeysForUser, updatePasskeyName, verifyAndAuthenticatePasskey, verifyAndCreatePasskey, } from "./lib/passkey"; import { requireAuth } from "./lib/middleware"; import { decrementCounter, getCounterForUser, incrementCounter, resetCounter, } from "./lib/counter"; const port = 3000; Bun.serve({ port, routes: { "/": indexHTML, // Auth endpoints "/api/auth/me": { GET: (req) => { try { const sessionId = getSessionFromRequest(req); if (!sessionId) { return new Response( JSON.stringify({ error: "Not authenticated" }), { status: 401, }, ); } const user = getUserBySession(sessionId); if (!user) { return new Response(JSON.stringify({ error: "Invalid session" }), { status: 401, }); } return new Response(JSON.stringify(user), { headers: { "Content-Type": "application/json" }, }); } catch (error) { return new Response( JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error", }), { status: 500 }, ); } }, }, "/api/auth/check-email": { GET: (req) => { try { const url = new URL(req.url); const username = url.searchParams.get("username"); if (!username) { return new Response( JSON.stringify({ error: "Username required" }), { status: 400, }, ); } const existing = getUserByUsername(username); if (existing) { return new Response( JSON.stringify({ error: "Username already taken" }), { status: 400 }, ); } return new Response(JSON.stringify({ available: true }), { headers: { "Content-Type": "application/json" }, }); } catch (error) { return new Response( JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error", }), { status: 500 }, ); } }, }, "/api/auth/register": { POST: async (req) => { try { const body = await req.json(); const { username, credential, challenge } = body; if (!username || !credential || !challenge) { return new Response( JSON.stringify({ error: "Username, credential, and challenge required", }), { status: 400 }, ); } // Check if user already exists const existing = getUserByUsername(username); if (existing) { return new Response( JSON.stringify({ error: "Username already taken" }), { status: 400 }, ); } // Create user const user = await createUser(username); // Verify and create passkey await verifyAndCreatePasskey(user.id, credential, challenge); // Create session const sessionId = createSession(user.id); return new Response(JSON.stringify(user), { headers: { "Content-Type": "application/json", "Set-Cookie": `session=${sessionId}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${7 * 24 * 60 * 60}`, }, }); } catch (error) { return new Response( JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error", }), { status: 500 }, ); } }, }, "/api/auth/logout": { POST: (req) => { try { const sessionId = getSessionFromRequest(req); if (sessionId) { deleteSession(sessionId); } return new Response(JSON.stringify({ success: true }), { headers: { "Content-Type": "application/json", "Set-Cookie": "session=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0", }, }); } catch (error) { return new Response( JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error", }), { status: 500 }, ); } }, }, // Passkey endpoints "/api/auth/passkey/register/options": { GET: async (req) => { try { // For registration, we need username from query params (no session yet) const url = new URL(req.url); const username = url.searchParams.get("username"); if (!username) { return new Response( JSON.stringify({ error: "Username required" }), { status: 400, }, ); } // Create temporary user object for registration options const tempUser = { id: 0, // Temporary ID username, name: null, avatar: "temp", created_at: Math.floor(Date.now() / 1000), }; const options = await createRegistrationOptions(tempUser); return new Response(JSON.stringify(options), { headers: { "Content-Type": "application/json" }, }); } catch (error) { return new Response( JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error", }), { status: 500 }, ); } }, }, "/api/auth/passkey/authenticate/options": { GET: async (req) => { try { const options = await createAuthenticationOptions(); return new Response(JSON.stringify(options), { headers: { "Content-Type": "application/json" }, }); } catch (error) { return new Response( JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error", }), { status: 500 }, ); } }, }, "/api/auth/passkey/authenticate/verify": { POST: async (req) => { try { const body = await req.json(); const { credential, challenge } = body; if (!credential || !challenge) { return new Response( JSON.stringify({ error: "Credential and challenge required" }), { status: 400 }, ); } const { userId } = await verifyAndAuthenticatePasskey( credential, challenge, ); const user = getUserBySession( createSession( userId, req.headers.get("x-forwarded-for") || undefined, ), ); if (!user) { return new Response(JSON.stringify({ error: "User not found" }), { status: 404, }); } const sessionId = createSession(userId); return new Response(JSON.stringify(user), { headers: { "Content-Type": "application/json", "Set-Cookie": `session=${sessionId}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${7 * 24 * 60 * 60}`, }, }); } catch (error) { return new Response( JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error", }), { status: 500 }, ); } }, }, // Counter endpoints "/api/counter": { GET: async (req) => { try { const userId = await requireAuth(req); const count = getCounterForUser(userId); return new Response(JSON.stringify({ count }), { headers: { "Content-Type": "application/json" }, }); } catch (error) { return new Response( JSON.stringify({ error: error instanceof Error ? error.message : "Not authenticated", }), { status: 401 }, ); } }, }, "/api/counter/increment": { POST: async (req) => { try { const userId = await requireAuth(req); const count = incrementCounter(userId); return new Response(JSON.stringify({ count }), { headers: { "Content-Type": "application/json" }, }); } catch (error) { return new Response( JSON.stringify({ error: error instanceof Error ? error.message : "Not authenticated", }), { status: 401 }, ); } }, }, "/api/counter/decrement": { POST: async (req) => { try { const userId = await requireAuth(req); const count = decrementCounter(userId); return new Response(JSON.stringify({ count }), { headers: { "Content-Type": "application/json" }, }); } catch (error) { return new Response( JSON.stringify({ error: error instanceof Error ? error.message : "Not authenticated", }), { status: 401 }, ); } }, }, "/api/counter/reset": { POST: async (req) => { try { const userId = await requireAuth(req); resetCounter(userId); return new Response(JSON.stringify({ count: 0 }), { headers: { "Content-Type": "application/json" }, }); } catch (error) { return new Response( JSON.stringify({ error: error instanceof Error ? error.message : "Not authenticated", }), { status: 401 }, ); } }, }, }, development: { hmr: true, console: true, }, }); console.log(`🥞 Tacy Stack running at http://localhost:${port}`);