queue-med/server/services/email.ts

61 lines
2.8 KiB
TypeScript

import nodemailer, { type Transporter } from "nodemailer";
import { childLogger } from "../_core/logger.js";
const log = childLogger("email");
let cachedTransporter: Transporter | null = null;
function getTransporter(): Transporter | null {
if (cachedTransporter) return cachedTransporter;
const host = process.env.SMTP_HOST;
const port = process.env.SMTP_PORT ? Number(process.env.SMTP_PORT) : undefined;
const user = process.env.SMTP_USER;
const pass = process.env.SMTP_PASS;
if (!host || !port) {
log.warn("SMTP_HOST/SMTP_PORT not configured — emails will be logged only");
return null;
}
cachedTransporter = nodemailer.createTransport({
host,
port,
secure: port === 465,
auth: user && pass ? { user, pass } : undefined,
});
return cachedTransporter;
}
export async function sendMail(opts: { to: string; subject: string; html: string; text?: string }): Promise<void> {
const from = process.env.SMTP_FROM ?? process.env.SMTP_USER ?? "no-reply@queuemed.app";
const transporter = getTransporter();
if (!transporter) {
log.info({ to: opts.to, subject: opts.subject }, "(dev) would send email");
return;
}
await transporter.sendMail({
from,
to: opts.to,
subject: opts.subject,
html: opts.html,
text: opts.text,
});
}
export function buildResetEmail(resetUrl: string): { subject: string; html: string; text: string } {
const subject = "QueueMed — Réinitialisation de votre mot de passe";
const text = `Bonjour,\n\nVous avez demandé à réinitialiser votre mot de passe QueueMed.\n\nCliquez sur ce lien (valable 1 heure) :\n${resetUrl}\n\nSi vous n'êtes pas à l'origine de cette demande, ignorez ce message.\n\n— L'équipe QueueMed`;
const html = `<!doctype html>
<html><body style="font-family:Inter,Arial,sans-serif;color:#0f172a;background:#f0fdf4;margin:0;padding:24px">
<div style="max-width:540px;margin:0 auto;background:white;border-radius:16px;padding:32px;box-shadow:0 4px 20px rgba(16,185,129,0.1)">
<h1 style="color:#10b981;margin:0 0 16px;font-size:22px">Réinitialisation de votre mot de passe</h1>
<p>Bonjour,</p>
<p>Vous avez demandé à réinitialiser votre mot de passe QueueMed. Cliquez sur le bouton ci-dessous (valable 1 heure) :</p>
<p style="margin:24px 0">
<a href="${resetUrl}" style="display:inline-block;padding:12px 24px;background:linear-gradient(135deg,#10b981,#06b6d4);color:white;text-decoration:none;border-radius:12px;font-weight:600">Réinitialiser mon mot de passe</a>
</p>
<p style="color:#64748b;font-size:13px">Si vous n'êtes pas à l'origine de cette demande, ignorez ce message.</p>
<hr style="border:none;border-top:1px solid #e2e8f0;margin:24px 0" />
<p style="color:#94a3b8;font-size:12px">— L'équipe QueueMed</p>
</div>
</body></html>`;
return { subject, html, text };
}