SPB Git

spb/phi-conseil

Public
2.7 KB · 86 lines typescript
1import nodemailer from "nodemailer";2import type { ContactData } from "./validation";3import { getEnv } from "./env";45export interface Mailer {6  send(data: ContactData, meta: { ipHash: string }): Promise<{ delivered: boolean; id?: string }>;7}89function escapeHtml(s: string) {10  return s11    .replace(/&/g, "&amp;")12    .replace(/</g, "&lt;")13    .replace(/>/g, "&gt;")14    .replace(/"/g, "&quot;");15}1617export function renderContactEmail(data: ContactData) {18  const text = [19    `Nouveau message depuis le site PHI Conseil`,20    ``,21    `Nom : ${data.name}`,22    `Courriel : ${data.email}`,23    `Organisation : ${data.organization || "—"}`,24    `Sujet : ${data.subject}`,25    ``,26    data.message,27  ].join("\n");28  const html = `<!doctype html><html lang="fr"><body style="font-family:system-ui,sans-serif;color:#1f2a33">29<h2 style="font-weight:600">Nouveau message depuis le site PHI Conseil</h2>30<table cellpadding="4">31<tr><td><strong>Nom</strong></td><td>${escapeHtml(data.name)}</td></tr>32<tr><td><strong>Courriel</strong></td><td>${escapeHtml(data.email)}</td></tr>33<tr><td><strong>Organisation</strong></td><td>${escapeHtml(data.organization || "—")}</td></tr>34<tr><td><strong>Sujet</strong></td><td>${escapeHtml(data.subject)}</td></tr>35</table>36<p style="white-space:pre-wrap">${escapeHtml(data.message)}</p>37</body></html>`;38  return { subject: `[Site PHI Conseil] ${data.subject}`, text, html };39}4041/** Transport « log » : n'envoie rien, journalise une empreinte. Jamais présenté comme une livraison. */42export const logMailer: Mailer = {43  async send(data, meta) {44    console.info(45      `[contact] CONTACT_TRANSPORT=log — courriel NON envoyé (sujet="${data.subject}", ip=${meta.ipHash})`,46    );47    return { delivered: false };48  },49};5051export function createSmtpMailer(): Mailer {52  const env = getEnv();53  const transporter = nodemailer.createTransport({54    host: env.SMTP_HOST,55    port: env.SMTP_PORT,56    secure: env.SMTP_SECURE,57    auth: env.SMTP_USER ? { user: env.SMTP_USER, pass: env.SMTP_PASS } : undefined,58  });59  return {60    async send(data) {61      const { subject, text, html } = renderContactEmail(data);62      const info = await transporter.sendMail({63        from: env.CONTACT_FROM,64        to: env.CONTACT_TO,65        replyTo: data.email,66        subject,67        text,68        html,69      });70      return { delivered: true, id: info.messageId };71    },72  };73}7475let instance: Mailer | null = null;76export function getMailer(): Mailer {77  if (instance) return instance;78  instance = getEnv().CONTACT_TRANSPORT === "smtp" ? createSmtpMailer() : logMailer;79  return instance;80}8182/** Pour les tests. */83export function setMailerForTests(m: Mailer | null) {84  instance = m;85}86
Files · main
Menu

Theme