@@ -11,6 +11,7 @@ import {EmailService} from '../services/EmailService.js';
1111import { EventService } from '../services/EventService.js' ;
1212import { NotFound , ValidationError } from '../exceptions/index.js' ;
1313import { 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