Initial commit

This commit is contained in:
2026-08-02 20:55:23 +02:00
parent 411f484b40
commit d558d18d9a
295 changed files with 9 additions and 5880 deletions
-83
View File
@@ -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