// Route handler server-side para envío de correos vía banco único mail.*. // El navegador NUNCA lleva la X-Api-Key: llama aquí (/api/correos) y este // route llama a SAM.API (POST /api/email/send) con X-Module + X-Api-Key. // Docs: sam/docs/email-modulos.md punto 7. // // Env (servidor, SIN prefijo NEXT_PUBLIC_): // MAIL_API_KEY=clave entregada por POST /api/email/modules (solo servidor) // MAIL_MODULE=helpdesk (opcional, default helpdesk) // SAM_API_URL=https://api.devops.norteamericano.cl (default prod; // en dev cae a NEXT_PUBLIC_API_URL) const API_BASE = process.env.SAM_API_URL ?? process.env.NEXT_PUBLIC_API_URL ?? "https://api.devops.norteamericano.cl"; const MODULE = process.env.MAIL_MODULE ?? "helpdesk"; type SendBody = { to: string | string[]; templateKey?: string; subject?: string; htmlBody?: string; vars?: Record; }; const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; async function requireAdmin(req: Request): Promise { const auth = req.headers.get("authorization") ?? ""; if (!auth.toLowerCase().startsWith("bearer ")) { return Response.json( { success: false, message: "Requiere sesión de admin" }, { status: 401 } ); } try { const me = await fetch(`${API_BASE}/api/helpdesk/me`, { headers: { Authorization: auth }, }); if (!me.ok) { return Response.json( { success: false, message: "Sesión inválida" }, { status: 401 } ); } const user = (await me.json()) as { role?: string }; if (user.role !== "admin") { return Response.json( { success: false, message: "Requiere rol admin" }, { status: 403 } ); } return null; } catch (err) { return Response.json( { success: false, message: `No se pudo verificar admin: ${String(err)}` }, { status: 502 } ); } } export async function POST(req: Request): Promise { const denied = await requireAdmin(req); if (denied) return denied; const apiKey = process.env.MAIL_API_KEY ?? ""; if (!apiKey) { return Response.json( { success: false, message: "MAIL_API_KEY no configurada en el servidor del módulo" }, { status: 500 } ); } let body: SendBody; try { body = (await req.json()) as SendBody; } catch { return Response.json({ success: false, message: "JSON inválido" }, { status: 400 }); } const raw = Array.isArray(body.to) ? body.to : [body.to]; const clean = [ ...new Set( raw.map((e) => String(e ?? "").trim()).filter((e) => EMAIL_RE.test(e)) ), ].slice(0, 50); if (clean.length === 0) { return Response.json({ success: false, message: "Sin destinatarios válidos" }, { status: 400 }); } if (!body.templateKey && (!body.subject || !body.htmlBody)) { return Response.json( { success: false, message: "templateKey o subject+htmlBody requeridos" }, { status: 400 } ); } let upstream: Response; try { upstream = await fetch(`${API_BASE}/api/email/send`, { method: "POST", headers: { "Content-Type": "application/json", "X-Module": MODULE, "X-Api-Key": apiKey, }, body: JSON.stringify({ to: clean.length === 1 ? clean[0] : clean, templateKey: body.templateKey, subject: body.subject, htmlBody: body.htmlBody, vars: body.vars ?? {}, }), }); } catch (err) { return Response.json( { success: false, message: `No se pudo contactar a SAM.API: ${String(err)}` }, { status: 502 } ); } let data: unknown = null; try { data = await upstream.json(); } catch { data = { success: false, message: `SAM.API respondió ${upstream.status} sin JSON` }; } return Response.json(data, { status: upstream.ok ? 200 : upstream.status }); }