import { Router } from 'express' import { z } from 'zod' import { parseBody } from '../../http/validate' import { ok } from '../../http/respond' import { AppError } from '../../http/errors' import { setSessionCookie, clearSessionCookie } from '../../security/sessionCookies' import * as employeeService from './auth.employee.service' import * as adminService from '../admin/admin.service' const unifiedLoginSchema = z.object({ email: z.string().email().max(255).trim().toLowerCase(), password: z.string().max(128), totpCode: z.string().length(6).optional(), recoveryCode: z.string().min(8).max(32).optional(), }) const router = Router() router.post('/login', async (req, res, next) => { try { const { email, password, totpCode, recoveryCode } = parseBody(unifiedLoginSchema, req) if (!totpCode && !recoveryCode) { try { const employeeResult = await employeeService.login({ email, password }) if (!('token' in employeeResult)) { throw new AppError('Invalid email or password', 401, 'invalid_credentials') } clearSessionCookie(res, 'admin') setSessionCookie(res, 'employee', employeeResult.token, 8 * 60 * 60 * 1000) return ok(res, employeeResult) } catch (err) { if (!(err instanceof AppError) || err.error !== 'invalid_credentials') throw err } } const adminResult = await adminService.login(email, password, totpCode, recoveryCode) if (!adminResult) { return res.status(401).json({ error: 'invalid_credentials', message: 'Invalid email or password', statusCode: 401 }) } if ('totpRequired' in adminResult) { clearSessionCookie(res, 'employee') return res.status(401).json({ error: 'totp_required', message: '2FA code required', statusCode: 401 }) } if ('invalidTotp' in adminResult) { return res.status(401).json({ error: 'invalid_totp', message: 'Invalid 2FA code', statusCode: 401 }) } clearSessionCookie(res, 'employee') setSessionCookie(res, 'admin', adminResult.token, 8 * 60 * 60 * 1000) return ok(res, adminResult) } catch (err) { return next(err) } }) export default router