Skip to content

Commit 8c03042

Browse files
committed
fix: Variable substitution in transactional emails
1 parent dc9b88d commit 8c03042

1 file changed

Lines changed: 13 additions & 1 deletion

File tree

apps/api/src/controllers/Actions.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {EmailService} from '../services/EmailService.js';
1111
import {EventService} from '../services/EventService.js';
1212
import {NotFound, ValidationError} from '../exceptions/index.js';
1313
import {CatchAsync} from '../utils/asyncHandler.js';
14+
import {DASHBOARD_URI} from '../app/constants.js';
1415

1516
/**
1617
* Public API Actions Controller
@@ -271,12 +272,23 @@ export class Actions {
271272
// Get merged data including non-persistent fields for template rendering
272273
const mergedData = ContactService.getMergedData(contact, data as Record<string, unknown> | undefined);
273274

275+
// Add system variables (email, unsubscribe URLs, etc.) to merged data
276+
// These are always available for template rendering
277+
const dataWithSystemVars = {
278+
...mergedData,
279+
email: contact.email,
280+
data: mergedData, // Also available as nested data for {{data.fieldName}} syntax
281+
unsubscribeUrl: `${DASHBOARD_URI}/unsubscribe/${contact.id}`,
282+
subscribeUrl: `${DASHBOARD_URI}/subscribe/${contact.id}`,
283+
manageUrl: `${DASHBOARD_URI}/manage/${contact.id}`,
284+
};
285+
274286
// Render template with contact data
275287
// Simple template variable replacement: {{fieldname}}
276288
let renderedSubject = emailSubject!;
277289
let renderedBody = emailBody!;
278290

279-
for (const [key, value] of Object.entries(mergedData)) {
291+
for (const [key, value] of Object.entries(dataWithSystemVars)) {
280292
const placeholder = new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, 'g');
281293
const fallbackPlaceholder = new RegExp(`\\{\\{\\s*${key}\\s*\\?\\?\\s*([^}]+)\\}\\}`, 'g');
282294

0 commit comments

Comments
 (0)