Merge pull request #836 from Hexastack/feat/use-handlebars-infavor-of-ejs
Some checks are pending
Build and Push Docker API Image / build-and-push (push) Waiting to run
Build and Push Docker Base Image / build-and-push (push) Waiting to run
Build and Push Docker UI Image / build-and-push (push) Waiting to run

feat: use handlebars as template engine for mailing & remove ejs
This commit is contained in:
Med Marrouchi 2025-03-21 01:27:27 +01:00 committed by GitHub
commit 5e31cfe459
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 91 additions and 59 deletions

9
api/package-lock.json generated
View File

@ -33,7 +33,6 @@
"connect-mongo": "^5.1.0",
"cookie-parser": "^1.4.6",
"dotenv": "^16.3.1",
"ejs": "^3.1.9",
"express-session": "^1.17.3",
"handlebars": "^4.7.8",
"module-alias": "^2.2.3",
@ -7408,7 +7407,8 @@
"node_modules/async": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
"optional": true
},
"node_modules/async-mutex": {
"version": "0.4.1",
@ -9629,6 +9629,7 @@
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
"optional": true,
"dependencies": {
"jake": "^10.8.5"
},
@ -10805,6 +10806,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
"optional": true,
"dependencies": {
"minimatch": "^5.0.1"
}
@ -10813,6 +10815,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"optional": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
@ -10821,6 +10824,7 @@
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"optional": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
@ -12526,6 +12530,7 @@
"version": "10.8.7",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz",
"integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==",
"optional": true,
"dependencies": {
"async": "^3.2.3",
"chalk": "^4.0.2",

View File

@ -68,7 +68,6 @@
"connect-mongo": "^5.1.0",
"cookie-parser": "^1.4.6",
"dotenv": "^16.3.1",
"ejs": "^3.1.9",
"express-session": "^1.17.3",
"handlebars": "^4.7.8",
"module-alias": "^2.2.3",

View File

@ -71,7 +71,15 @@ const i18nOptions: I18nOptions = {
debug: false,
}),
template: {
adapter: new MjmlAdapter('ejs', { inlineCssEnabled: false }),
adapter: new MjmlAdapter(
'handlebars',
{
inlineCssEnabled: false,
},
{
handlebar: {},
},
),
dir: path.join(process.cwd(), 'dist', 'templates'),
options: {
context: {

View File

@ -2,34 +2,38 @@
<mj-body>
<mj-section>
<mj-column>
<mj-image width="186px" src="https://hexabot.ai/assets/images/logo.png"></mj-image>
<mj-image
width="186px"
src="https://hexabot.ai/assets/images/logo.png"
></mj-image>
<mj-divider border-color="#000"></mj-divider>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= t('hi') %> <%= first_name %>,</mj-text
>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= t('account_successfully_created_confirm_password') %></mj-text
>
<mj-text font-size="16px" color="#000" font-family="helvetica">
{{t 'hi'}} {{first_name}},
</mj-text>
<mj-text font-size="16px" color="#000" font-family="helvetica">
{{t 'account_successfully_created_confirm_password'}}
</mj-text>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= t('confirm_account') %></mj-text
>
<mj-text font-size="16px" color="#000" font-family="helvetica">
{{t 'confirm_account'}}
</mj-text>
<mj-button
href="<%= this.appUrl %>/login/<%= token %>"
href="{{appUrl}}/login/{{token}}"
font-size="16px"
background-color="#000"
><%= t('confirm') %></mj-button
>
{{t 'confirm'}}
</mj-button>
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= t('best_regards') %></mj-text
>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= this.appName %></mj-text
>
<mj-text font-size="16px" color="#000" font-family="helvetica">
{{t 'best_regards'}}
</mj-text>
<mj-text font-size="16px" color="#000" font-family="helvetica">
{{appName}}
</mj-text>
</mj-column>
</mj-section>
</mj-body>

View File

@ -2,36 +2,39 @@
<mj-body>
<mj-section>
<mj-column>
<mj-image width="186px" src="https://hexabot.ai/assets/images/logo.png"></mj-image>
<mj-image
width="186px"
src="https://hexabot.ai/assets/images/logo.png"
></mj-image>
<mj-divider border-color="#000"></mj-divider>
<mj-text font-size="16px" color="#000" font-family="helvetica">
<%= t('welcome') %>,
{{t 'welcome'}}
</mj-text>
<mj-text font-size="16px" color="#000" font-family="helvetica">
{{t 'invitation_for_account_creation'}} {{appName}} {{t 'account'}}.
</mj-text>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= `${t('invitation_for_account_creation')} ${this.appName}
${t('account')}.` %></mj-text
>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= t('create_account') %></mj-text
>{{ t 'create_account'}}</mj-text
>
<mj-button
href="<%= this.appUrl %>/register/<%= token %>"
href="{{appUrl}}/register/{{token}}"
font-size="16px"
background-color="#000"
><%= t('join') %></mj-button
>
{{t 'join'}}</mj-button
>
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= t('best_regards') %></mj-text
>
>{{t 'best_regards'}}
</mj-text>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= this.appName %></mj-text
>{{this.appName }}</mj-text
>
</mj-column>
</mj-section>

View File

@ -2,33 +2,37 @@
<mj-body>
<mj-section>
<mj-column>
<mj-image width="186px" src="https://hexabot.ai/assets/images/logo.png"></mj-image>
<mj-image
width="186px"
src="https://hexabot.ai/assets/images/logo.png"
></mj-image>
<mj-divider border-color="#000"></mj-divider>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= t('hi') %> <%= first_name %>,</mj-text
>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= t('password_reset_request') %></mj-text
>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= t('click_to_reset_password') %></mj-text
>
<mj-text font-size="16px" color="#000" font-family="helvetica">
{{t 'hi'}} {{first_name}},
</mj-text>
<mj-text font-size="16px" color="#000" font-family="helvetica">
{{t 'password_reset_request'}}
</mj-text>
<mj-text font-size="16px" color="#000" font-family="helvetica">
{{t 'click_to_reset_password'}}
</mj-text>
<mj-button
href="<%= this.appUrl %>/reset/<%= token %>"
href="{{appUrl}}/reset/{{token}}"
font-size="16px"
background-color="#000"
><%= t('reset') %></mj-button
>
{{t 'reset'}}
</mj-button>
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= t('best_regards') %></mj-text
>
<mj-text font-size="16px" color="#000" font-family="helvetica"
><%= this.appName %></mj-text
>
<mj-text font-size="16px" color="#000" font-family="helvetica">
{{t 'best_regards'}}
</mj-text>
<mj-text font-size="16px" color="#000" font-family="helvetica">
{{appName}}
</mj-text>
</mj-column>
</mj-section>
</mj-body>

View File

@ -1,11 +1,13 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
// eslint-disable-next-line import/order
import { MailerService } from '@nestjs-modules/mailer';
import {
Inject,
Injectable,
@ -13,7 +15,6 @@ import {
Optional,
} from '@nestjs/common';
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
import { MailerService } from '@nestjs-modules/mailer';
import { config } from '@/config';
import { I18nService } from '@/i18n/services/i18n.service';
@ -69,6 +70,8 @@ export class InvitationService extends BaseService<
to: dto.email,
template: 'invitation.mjml',
context: {
appName: config.parameters.appName,
appUrl: config.uiBaseUrl,
token: jwt,
// TODO: Which language should we use?
t: (key: string) =>

View File

@ -1,11 +1,13 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
// eslint-disable-next-line import/order
import { MailerService } from '@nestjs-modules/mailer';
import {
BadRequestException,
Inject,
@ -16,7 +18,6 @@ import {
UnauthorizedException,
} from '@nestjs/common';
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
import { MailerService } from '@nestjs-modules/mailer';
import { compareSync } from 'bcryptjs';
import { config } from '@/config';
@ -66,6 +67,8 @@ export class PasswordResetService {
to: dto.email,
template: 'password_reset.mjml',
context: {
appName: config.parameters.appName,
appUrl: config.uiBaseUrl,
token: jwt,
first_name: user.first_name,
t: (key: string) =>

View File

@ -1,11 +1,13 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
// eslint-disable-next-line import/order
import { MailerService } from '@nestjs-modules/mailer';
import {
Inject,
Injectable,
@ -14,7 +16,6 @@ import {
UnauthorizedException,
} from '@nestjs/common';
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
import { MailerService } from '@nestjs-modules/mailer';
import { config } from '@/config';
import { I18nService } from '@/i18n/services/i18n.service';
@ -83,6 +84,8 @@ export class ValidateAccountService {
to: dto.email,
template: 'account_confirmation.mjml',
context: {
appName: config.parameters.appName,
appUrl: config.uiBaseUrl,
token: confirmationToken,
first_name: dto.first_name,
t: (key: string) =>