import { wrapAsyncHandler } from "@batch-cooking/express-tools"; import { adminLoginSchema } from "@batch-cooking/shared"; import { type CookieOptions, type Response, Router } from "express"; import { env } from "../../config/env.js"; import { type AdminLocals, requireAdmin } from "../../middlewares/require-admin.js"; import { adminLogin } from "./admin-auth.service.js"; /** Router mounted at `/admin/auth` (via `admin.routes.ts`) — admin login, logout, current-admin. No signup: admins are created out-of-band (`src/scripts/create-admin.ts`). */ export const adminAuthRouter = Router(); const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000; /** * Cookie options for the admin session — same shape as `auth.routes.ts`'s * end-user cookie (httpOnly, `Secure` in production unless `COOKIE_SECURE` * overrides, `SameSite=Lax`), just written under {@link env.ADMIN_COOKIE_NAME} * so the two sessions never collide in one browser. */ const adminCookieOptions: CookieOptions = { httpOnly: true, secure: env.COOKIE_SECURE ?? env.NODE_ENV === "production", sameSite: "lax", maxAge: SEVEN_DAYS_MS, }; // `res.clearCookie` sets its own expiry — passing `maxAge` alongside is // deprecated as of Express 4.20, so the logout route reuses the options // minus that one field (same trick as `auth.routes.ts`). const { maxAge: _maxAge, ...clearAdminCookieOptions } = adminCookieOptions; /** Verifies admin credentials and starts an admin session. */ adminAuthRouter.post( "/login", wrapAsyncHandler(async (req, res) => { const input = adminLoginSchema.parse(req.body); const { admin, token } = await adminLogin(input); res.cookie(env.ADMIN_COOKIE_NAME, token, adminCookieOptions); res.status(200).json(admin); }), ); /** Ends the admin session by clearing the cookie. Stateless JWT — nothing to revoke server-side beyond bumping `tokenVersion` (no UI for that yet). */ adminAuthRouter.post("/logout", (_req, res) => { res.clearCookie(env.ADMIN_COOKIE_NAME, clearAdminCookieOptions); res.status(204).end(); }); /** Returns the currently authenticated admin. Behind `requireAdmin` — 401s if there's no valid admin session. */ adminAuthRouter.get("/me", requireAdmin, (_req, res: Response) => { res.status(200).json(res.locals.adminUser); });