Initial commit
This commit is contained in:
-83
@@ -1,83 +0,0 @@
|
||||
import { getSiteIntegrations } from '../payload/index.js';
|
||||
/**
|
||||
* Payload email adapter backed by the SMTP settings in the SiteIntegrations
|
||||
* global.
|
||||
*
|
||||
* Why this exists: Payload builds its email adapter once, at boot, from the
|
||||
* config — which would normally mean SMTP credentials living in env vars and a
|
||||
* redeploy to change them. This adapter instead resolves SMTP on every send, so
|
||||
* an editor can change the mailbox in the admin panel and the next email uses
|
||||
* it.
|
||||
*
|
||||
* Wiring it into the config means `payload.sendEmail` works everywhere — which
|
||||
* includes the form-builder's own submission emails (the ones an editor
|
||||
* configures per form under "Emails"). Those go out over the panel's SMTP with
|
||||
* no extra code.
|
||||
*
|
||||
* Boot-time constraints shape two details:
|
||||
* - `defaultFromAddress` / `defaultFromName` must be returned synchronously, so
|
||||
* they're placeholders; the real from-address is applied per message below.
|
||||
* - nodemailer is imported dynamically inside sendEmail, so merely loading the
|
||||
* Payload config (or running `generate:importmap`) doesn't pull it in.
|
||||
*/ export const panelSmtpAdapter = (args = {})=>({ payload })=>({
|
||||
name: 'ipal-panel-smtp',
|
||||
defaultFromAddress: args.fallbackFromAddress ?? 'noreply@localhost',
|
||||
defaultFromName: args.fallbackFromName ?? 'Website',
|
||||
sendEmail: async (message)=>{
|
||||
const smtp = await getSiteIntegrations(payload);
|
||||
const host = smtp.smtpHost;
|
||||
const port = smtp.smtpPort ?? 587;
|
||||
const user = smtp.smtpUser;
|
||||
const pass = smtp.smtpPassword;
|
||||
if (!host || !user || !pass) {
|
||||
payload.logger.error('[ipal] Email not sent: SMTP is not configured in Site Integrations.');
|
||||
return {
|
||||
error: 'SMTP is not configured in Site Integrations.',
|
||||
sent: false
|
||||
};
|
||||
}
|
||||
// The panel is the source of truth for the sender; fall back to whatever
|
||||
// the caller set (Payload fills in defaultFromAddress when unset).
|
||||
const fromAddress = smtp.smtpFromAddress;
|
||||
const fromName = smtp.smtpFromName;
|
||||
const from = fromAddress ? fromName ? `${fromName} <${fromAddress}>` : fromAddress : message.from;
|
||||
// Defence in depth against header injection. Modern nodemailer already
|
||||
// normalises CRLF in standard headers, but the form-builder interpolates
|
||||
// user data into the subject via {{field}} placeholders, so strip any
|
||||
// newlines from header-bound values before they reach the transport.
|
||||
const stripCRLF = (v)=>String(v ?? '').replace(/[\r\n]+/g, ' ').trim();
|
||||
const { default: nodemailer } = await import('nodemailer');
|
||||
const transporter = nodemailer.createTransport({
|
||||
auth: {
|
||||
pass,
|
||||
user
|
||||
},
|
||||
host,
|
||||
port,
|
||||
secure: port === 465
|
||||
});
|
||||
try {
|
||||
const info = await transporter.sendMail({
|
||||
...message,
|
||||
from,
|
||||
...message.subject ? {
|
||||
subject: stripCRLF(message.subject)
|
||||
} : {}
|
||||
});
|
||||
return {
|
||||
messageId: info.messageId,
|
||||
sent: true
|
||||
};
|
||||
} catch (err) {
|
||||
// Never surface SMTP internals to the caller — a form submission
|
||||
// shouldn't fail loudly because the mailbox is misconfigured.
|
||||
payload.logger.error(`[ipal] Email send failed: ${err.message}`);
|
||||
return {
|
||||
error: 'Failed to send email.',
|
||||
sent: false
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//# sourceMappingURL=panelSmtpAdapter.js.map
|
||||
Reference in New Issue
Block a user