'use strict' const cfg = require('./config') const { log } = require('./helpers') let _transporter = null function getTransporter () { if (_transporter) return _transporter try { const nodemailer = require('nodemailer') if (cfg.SMTP_USER && cfg.SMTP_PASS) { _transporter = nodemailer.createTransport({ host: cfg.SMTP_HOST, port: cfg.SMTP_PORT, secure: cfg.SMTP_SECURE, auth: { user: cfg.SMTP_USER, pass: cfg.SMTP_PASS }, }) log(`Email transport configured: ${cfg.SMTP_HOST}:${cfg.SMTP_PORT} as ${cfg.SMTP_USER}`) } else { // Direct delivery (no SMTP relay) — sends directly to recipient MX _transporter = nodemailer.createTransport({ direct: true, name: 'msg.gigafibre.ca' }) log('Email transport: direct MX delivery (no SMTP relay configured)') } } catch (e) { log('Email transport init failed:', e.message) return null } return _transporter } /** * Send an HTML email with optional PDF attachment * @param {object} opts * @param {string} opts.to - Recipient email * @param {string} opts.subject - Subject line * @param {string} opts.html - HTML body * @param {Buffer} [opts.pdfBuffer] - Optional PDF attachment * @param {string} [opts.pdfFilename] - PDF filename * @returns {Promise} true if sent */ async function sendEmail (opts) { const transport = getTransporter() if (!transport) { log('Cannot send email — no transport available') return false } const mailOpts = { from: opts.from || cfg.MAIL_FROM, to: opts.to, subject: opts.subject, html: opts.html, attachments: [], // Custom headers (e.g. X-MJ-CustomID for Mailjet Event API webhook // correlation — Mailjet echoes the CustomID back in every event so // we can match webhook events to the originating recipient). headers: opts.headers || {}, } if (opts.pdfBuffer && opts.pdfFilename) { mailOpts.attachments.push({ filename: opts.pdfFilename, content: opts.pdfBuffer, contentType: 'application/pdf', }) } try { const info = await transport.sendMail(mailOpts) log(`Email sent to ${opts.to}: ${info.messageId || 'OK'}`) // Return the info object (always truthy) so callers can capture // info.messageId for tracking. Legacy `if (await sendEmail(...))` // callers continue to work because the object is truthy. return info || { messageId: null } } catch (e) { log(`Email send failed to ${opts.to}: ${e.message}`) // Legacy contract: return false on failure. New callers that need the // error string should check `Promise.allSettled` style or wrap in try // (we don't throw here to preserve existing `if (await sendEmail(...))` // call sites). The error is logged above. return false } } /** * Send a quotation acceptance email with PDF attached */ async function sendQuotationEmail (opts) { const { to, quotationName, acceptLink, pdfBuffer } = opts const html = `

Votre devis est prêt

Devis ${quotationName}

Bonjour,

Votre devis ${quotationName} est prêt pour votre examen. Cliquez sur le bouton ci-dessous pour le consulter et l'accepter.

Voir et accepter le devis

${pdfBuffer ? 'Le PDF du devis est également joint à ce courriel.' : ''} Ce lien est valide pour 7 jours.

Gigafibre — Targo Télécommunications

` return sendEmail({ to, subject: `Devis ${quotationName} — Acceptation requise`, html, pdfBuffer: pdfBuffer || null, pdfFilename: pdfBuffer ? `${quotationName}.pdf` : null, }) } module.exports = { sendEmail, sendQuotationEmail }