import { Elysia } from "elysia"; import { loginAdmin, logoutAdmin, requireAdminSession, getSessionFromRequest, seedAdminAccount } from "./auth/service"; import { cleanupDatabase } from "./lib/db"; import { errorResponse, jsonResponse, emptyResponse, optionsResponse, parseJson, assertAllowedOrigin } from "./lib/http"; import { config } from "./lib/config"; import { serveFrontendBuild } from "./lib/frontend"; import { createUserSchema, loginSchema, parseRouteEmail, resetPasswordSchema, resetTokenSchema, updatePasswordSchema } from "./lib/validation"; import { consumePasswordReset, createUser, deleteUser, inspectPasswordReset, listUsers, triggerPasswordReset, updateUserPassword } from "./users/service"; const app = new Elysia(); const route = ( handler: (context: { request: Request; params: Record; }) => Promise | Response ) => async ({ request, params }: { request: Request; params: Record }) => { if (request.method === "OPTIONS") { return optionsResponse(request); } try { assertAllowedOrigin(request); cleanupDatabase(); return await handler({ request, params }); } catch (error) { return errorResponse(request, error); } }; await seedAdminAccount(); app.get( "/api/health", route(async ({ request }) => jsonResponse(request, 200, { ok: true }) ) ); app.get( "/api/auth/session", route(async ({ request }) => jsonResponse(request, 200, { session: getSessionFromRequest(request) }) ) ); app.post( "/api/auth/login", route(async ({ request }) => { const body = await parseJson(request, loginSchema); const result = await loginAdmin(body.email, body.password); return jsonResponse( request, 200, { session: result.session }, { "set-cookie": result.cookie } ); }) ); app.post( "/api/auth/logout", route(async ({ request }) => jsonResponse( request, 200, { session: { authenticated: false, adminEmail: null } }, { "set-cookie": logoutAdmin(request) } ) ) ); app.get( "/api/users", route(async ({ request }) => { requireAdminSession(request); return jsonResponse(request, 200, { items: await listUsers() }); }) ); app.post( "/api/users", route(async ({ request }) => { const session = requireAdminSession(request); const body = await parseJson(request, createUserSchema); const result = await createUser(body.email, session.adminEmail); return jsonResponse(request, 201, result); }) ); app.patch( "/api/users/:email/password", route(async ({ request, params }) => { requireAdminSession(request); const email = parseRouteEmail(params.email); const body = await parseJson(request, updatePasswordSchema); await updateUserPassword(email, body.password); return emptyResponse(request, 204); }) ); app.delete( "/api/users/:email", route(async ({ request, params }) => { requireAdminSession(request); const email = parseRouteEmail(params.email); await deleteUser(email); return emptyResponse(request, 204); }) ); app.post( "/api/users/:email/password-reset", route(async ({ request, params }) => { const session = requireAdminSession(request); const email = parseRouteEmail(params.email); const result = await triggerPasswordReset(email, session.adminEmail); return jsonResponse(request, 200, result); }) ); app.post( "/api/password-resets/inspect", route(async ({ request }) => { const body = await parseJson(request, resetTokenSchema); return jsonResponse(request, 200, await inspectPasswordReset(body.token)); }) ); app.post( "/api/password-resets/consume", route(async ({ request }) => { const body = await parseJson(request, resetPasswordSchema); await consumePasswordReset(body.token, body.password); return emptyResponse(request, 204); }) ); app.get( "/", route(async ({ request }) => serveFrontendBuild(request)) ); app.get( "/*", route(async ({ request }) => serveFrontendBuild(request)) ); app.listen({ hostname: config.host, port: config.port }); console.log(`Mail admin API listening on http://${config.host}:${config.port}`);