fix: register mail layout partial on handlebars singleton for real mailer wiring
The HandlebarsAdapter reads partials config from a top-level sibling of
`template` (mailerOptions.options.partials), not from
template.options.partials where it was nested. Because the mail templates
use partial blocks ({{#> layout}}...{{/layout}}), the unregistered partial
rendered silently as an unstyled fragment instead of throwing, so this went
unnoticed. A config-only fix also breaks on Windows because the adapter's
glob-based directory loader mishandles backslash path separators.
Fix registers the shared `layout` partial directly on the handlebars module
singleton in MailConfigService, bypassing the broken glob loader entirely.
Also:
- add mail-config.service.spec.ts, an integration test that drives the real
MailerOptions + HandlebarsAdapter wiring (would have caught this bug,
unlike the existing template-only spec which registers the partial itself)
- remove stale nestjs-i18n references from .env.example, env-example, and
the backend README (i18n was already removed from the code)
- add missing trailing newlines to activation.hbs and reset-password.hbs
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as handlebars from 'handlebars';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { MailerOptions, MailerOptionsFactory } from '@nestjs-modules/mailer';
|
||||
@@ -9,6 +11,21 @@ export class MailConfigService implements MailerOptionsFactory {
|
||||
constructor(private configService: ConfigService) {}
|
||||
|
||||
createMailerOptions(): MailerOptions {
|
||||
const templatesDir = path.join(
|
||||
this.configService.get('app.workingDirectory'),
|
||||
'src',
|
||||
'mail',
|
||||
'mail-templates',
|
||||
);
|
||||
|
||||
handlebars.registerPartial(
|
||||
'layout',
|
||||
fs.readFileSync(
|
||||
path.join(templatesDir, 'partials', 'layout.hbs'),
|
||||
'utf-8',
|
||||
),
|
||||
);
|
||||
|
||||
return {
|
||||
transport: {
|
||||
host: this.configService.get('mail.host'),
|
||||
@@ -27,24 +44,10 @@ export class MailConfigService implements MailerOptionsFactory {
|
||||
)}" <${this.configService.get('mail.defaultEmail')}>`,
|
||||
},
|
||||
template: {
|
||||
dir: path.join(
|
||||
this.configService.get('app.workingDirectory'),
|
||||
'src',
|
||||
'mail',
|
||||
'mail-templates',
|
||||
),
|
||||
dir: templatesDir,
|
||||
adapter: new HandlebarsAdapter(),
|
||||
options: {
|
||||
strict: true,
|
||||
partials: {
|
||||
dir: path.join(
|
||||
this.configService.get('app.workingDirectory'),
|
||||
'src',
|
||||
'mail',
|
||||
'mail-templates',
|
||||
'partials',
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
} as MailerOptions;
|
||||
|
||||
Reference in New Issue
Block a user