agep-cargo/backend/postal.js
Antoine Pelletier 795eb30b0e feat: add email
2025-10-14 04:58:52 +02:00

80 lines
2.4 KiB
JavaScript

// Lightweight Postal API client
// Sends messages via Postal /api/v1/send/message
// Requires env: POSTAL_URL, POSTAL_API_KEY, POSTAL_FROM, optional POSTAL_FROM_NAME, POSTAL_REPLY_TO
import dotenv from 'dotenv'
dotenv.config()
let _fetch = null
async function getFetch() {
if (typeof globalThis.fetch === 'function') return globalThis.fetch
if (_fetch) return _fetch
try {
const mod = await import('node-fetch')
_fetch = mod.default
return _fetch
} catch (e) {
throw new Error('fetch is not available and node-fetch is not installed. Please install node-fetch or use Node 18+.')
}
}
function ensureArray(v) {
if (!v) return []
if (Array.isArray(v)) return v
if (typeof v === 'string') return v.split(/[;,]/).map(s => s.trim()).filter(Boolean)
return [String(v)]
}
function fmtFromAddress(email, name) {
const e = String(email || '').trim()
const n = String(name || '').trim()
if (!e) return ''
return n ? `${n} <${e}>` : e
}
export async function sendPostalEmail({ to, subject, text, html, cc = [], bcc = [] }) {
const base = (process.env.POSTAL_URL || '').replace(/\/$/, '')
const apiKey = process.env.POSTAL_API_KEY || ''
const fromEmail = process.env.POSTAL_FROM || ''
const fromName = process.env.POSTAL_FROM_NAME || ''
const replyTo = process.env.POSTAL_REPLY_TO || ''
if (!base || !apiKey || !fromEmail) {
throw new Error('Postal configuration missing: POSTAL_URL, POSTAL_API_KEY, POSTAL_FROM are required')
}
const endpoint = `${base}/api/v1/send/message`
const payload = {
api_key: apiKey,
from: fmtFromAddress(fromEmail, fromName),
to: ensureArray(to),
subject: subject || '',
html_body: html || undefined,
plain_body: text || undefined,
}
const ccArr = ensureArray(cc)
const bccArr = ensureArray(bcc)
if (ccArr.length) payload.cc = ccArr
if (bccArr.length) payload.bcc = bccArr
if (replyTo) payload.reply_to = replyTo
const fetchImpl = await getFetch()
const res = await fetchImpl(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
let data
try {
data = await res.json()
} catch (e) {
throw new Error(`Postal response parse error: HTTP ${res.status}`)
}
if (!res.ok || data?.status !== 'success') {
const errMsg = data?.data?.message || data?.message || JSON.stringify(data)
throw new Error(`Postal send failed: ${errMsg}`)
}
return data
}