/** * Envoi de courriels via Resend (API REST, aucune dépendance). * Variables : RESEND_API_KEY (obligatoire), MAIL_FROM (ex. « SPB Cloud »). * ⚠ Sans domaine vérifié dans Resend, seul « onboarding@resend.dev » est accepté comme expéditeur, * et il ne livre qu'à l'adresse propriétaire du compte Resend. */ export interface MailInput { to: string; subject: string; html: string; text: string; } export class MailerError extends Error { constructor(message: string, public status?: number, public code?: string) { super(message); } } export function mailerConfigured(): boolean { return !!process.env.RESEND_API_KEY; } export function mailFrom(): string { return process.env.MAIL_FROM || "SPB Cloud "; } export async function sendMail(input: MailInput): Promise<{ id: string }> { const key = process.env.RESEND_API_KEY; if (!key) throw new MailerError("RESEND_API_KEY manquante", 500, "not_configured"); const res = await fetch("https://api.resend.com/emails", { method: "POST", headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" }, body: JSON.stringify({ from: mailFrom(), to: [input.to], subject: input.subject, html: input.html, text: input.text }), }); const body = (await res.json().catch(() => ({}))) as { id?: string; message?: string; name?: string; statusCode?: number }; if (!res.ok) throw new MailerError(body.message || `Resend HTTP ${res.status}`, res.status, body.name); return { id: body.id || "" }; } /** Gabarit du code de connexion (clair, lisible sur mobile, marque SPB Cloud). */ export function loginCodeEmail(code: string, opts: { name?: string | null; minutes: number; ip?: string; device?: string }): { subject: string; html: string; text: string } { const spaced = code.split("").join(" "); const greeting = opts.name ? `Bonjour ${opts.name},` : "Bonjour,"; const subject = `${code} — votre code de connexion SPB Cloud`; const text = `${greeting}\n\nVoici votre code de connexion à SPB Cloud : ${code}\n\nIl expire dans ${opts.minutes} minutes et ne peut être utilisé qu'une fois.\nSi vous n'êtes pas à l'origine de cette demande, ignorez ce courriel.\n\n${opts.device ? `Appareil : ${opts.device}\n` : ""}${opts.ip ? `Adresse IP : ${opts.ip}\n` : ""}\n— SPB Cloud · cloud.spboucher.ai`; const html = `
SPB Cloud

${greeting}

Voici votre code de connexion. Il expire dans ${opts.minutes} minutes et ne sert qu'une fois.

${spaced}

Si vous n'êtes pas à l'origine de cette demande, ignorez simplement ce courriel — personne ne peut se connecter sans ce code.

${opts.device || opts.ip ? `

${opts.device ? `Appareil : ${opts.device}` : ""}${opts.device && opts.ip ? " · " : ""}${opts.ip ? `IP : ${opts.ip}` : ""}

` : ""}

SPB Cloud · cloud.spboucher.ai · cloud personnel privé et chiffré

`; return { subject, html, text }; }