This commit is contained in:
Yassine 2025-06-24 05:23:47 +00:00 committed by GitHub
commit b7bc84c09b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 183 additions and 126 deletions

View File

@ -9,14 +9,11 @@
import path from 'path';
import { CacheModule } from '@nestjs/cache-manager';
// eslint-disable-next-line import/order
import { MailerModule } from '@nestjs-modules/mailer';
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { MongooseModule } from '@nestjs/mongoose';
// eslint-disable-next-line import/order
import { MjmlAdapter } from '@nestjs-modules/mailer/dist/adapters/mjml.adapter';
import { CsrfGuard, CsrfModule } from '@tekuconcept/nestjs-csrf';
import { redisStore } from 'cache-manager-redis-yet';
import {
@ -24,7 +21,6 @@ import {
I18nOptions,
QueryResolver,
} from 'nestjs-i18n';
import SMTPTransport from 'nodemailer/lib/smtp-transport';
import { RedisClientOptions } from 'redis';
import { AnalyticsModule } from './analytics/analytics.module';
@ -40,6 +36,7 @@ import extraModules from './extra';
import { HelperModule } from './helper/helper.module';
import { I18nModule } from './i18n/i18n.module';
import { LoggerModule } from './logger/logger.module';
import { MailerModule } from './mailer/mailer.module';
import { MigrationModule } from './migration/migration.module';
import { NlpModule } from './nlp/nlp.module';
import { PluginsModule } from './plugins/plugins.module';
@ -63,36 +60,7 @@ const i18nOptions: I18nOptions = {
@Module({
imports: [
...(config.emails.isEnabled
? [
MailerModule.forRoot({
transport: new SMTPTransport({
...config.emails.smtp,
logger: true,
debug: false,
}),
template: {
adapter: new MjmlAdapter(
'handlebars',
{
inlineCssEnabled: false,
},
{
handlebar: {},
},
),
dir: path.join(process.cwd(), 'dist', 'templates'),
options: {
context: {
appName: config.parameters.appName,
appUrl: config.uiBaseUrl,
},
},
},
defaults: { from: config.emails.from },
}),
]
: []),
MailerModule,
MongooseModule.forRoot(config.mongo.uri, {
dbName: config.mongo.dbName,
connectionFactory: (connection) => {

View File

@ -0,0 +1,78 @@
/*
* 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).
*/
import path from 'path';
import { Global, Module } from '@nestjs/common';
import {
ISendMailOptions,
MAILER_OPTIONS,
MailerModule as NestjsMailerModule,
} from '@nestjs-modules/mailer';
import { MjmlAdapter } from '@nestjs-modules/mailer/dist/adapters/mjml.adapter';
import SMTPTransport from 'nodemailer/lib/smtp-transport';
import { config } from '@/config';
import { MailerService } from './mailer.service';
const mailerOptions = {
transport: new SMTPTransport({
...config.emails.smtp,
logger: true,
debug: false,
}),
template: {
adapter: new MjmlAdapter(
'handlebars',
{
inlineCssEnabled: false,
},
{
handlebar: {},
},
),
dir: path.join(process.cwd(), 'dist', 'templates'),
options: {
context: {
appName: config.parameters.appName,
appUrl: config.uiBaseUrl,
},
},
},
defaults: { from: config.emails.from },
};
@Global()
@Module({
imports: [
...(config.emails.isEnabled
? [NestjsMailerModule.forRoot(mailerOptions)]
: []),
],
providers: [
{
provide: MAILER_OPTIONS,
useValue: mailerOptions,
},
...(config.emails.isEnabled
? [MailerService]
: [
{
provide: MailerService,
useValue: {
sendMail(_options: ISendMailOptions) {
throw new Error('Email Service is not enabled');
},
},
},
]),
],
exports: [MailerService],
})
export class MailerModule {}

View File

@ -0,0 +1,19 @@
/*
* 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).
*/
import { MailerService as NestjsMailerService } from '@nestjs-modules/mailer';
/*
* 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).
*/
export class MailerService extends NestjsMailerService {}

View File

@ -12,10 +12,11 @@ import {
} from '@nestjs/common';
import { BadRequestException } from '@nestjs/common/exceptions/bad-request.exception';
import { JwtService } from '@nestjs/jwt';
import { ISendMailOptions, MailerService } from '@nestjs-modules/mailer';
import { ISendMailOptions } from '@nestjs-modules/mailer';
import { SentMessageInfo } from 'nodemailer';
import { I18nService } from '@/i18n/services/i18n.service';
import { MailerService } from '@/mailer/mailer.service';
import { getRandom } from '@/utils/helpers/safeRandom';
import { installLanguageFixtures } from '@/utils/test/fixtures/language';
import { installUserFixtures } from '@/utils/test/fixtures/user';

View File

@ -8,11 +8,12 @@
import { ForbiddenException, NotFoundException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ISendMailOptions, MailerService } from '@nestjs-modules/mailer';
import { ISendMailOptions } from '@nestjs-modules/mailer';
import { Session as ExpressSession } from 'express-session';
import { SentMessageInfo } from 'nodemailer';
import { I18nService } from '@/i18n/services/i18n.service';
import { MailerService } from '@/mailer/mailer.service';
import { IGNORED_TEST_FIELDS } from '@/utils/test/constants';
import { installLanguageFixtures } from '@/utils/test/fixtures/language';
import { installPermissionFixtures } from '@/utils/test/fixtures/permission';

View File

@ -7,10 +7,11 @@
*/
import { JwtModule, JwtService } from '@nestjs/jwt';
import { ISendMailOptions, MailerService } from '@nestjs-modules/mailer';
import { ISendMailOptions } from '@nestjs-modules/mailer';
import { SentMessageInfo } from 'nodemailer';
import { I18nService } from '@/i18n/services/i18n.service';
import { MailerService } from '@/mailer/mailer.service';
import { IGNORED_TEST_FIELDS } from '@/utils/test/constants';
import {
installInvitationFixtures,

View File

@ -6,19 +6,17 @@
* 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,
InternalServerErrorException,
Optional,
} from '@nestjs/common';
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
import { config } from '@/config';
import { I18nService } from '@/i18n/services/i18n.service';
import { LanguageService } from '@/i18n/services/language.service';
import { MailerService } from '@/mailer/mailer.service';
import { BaseService } from '@/utils/generics/base-service';
import { InvitationCreateDto } from '../dto/invitation.dto';
@ -41,7 +39,7 @@ export class InvitationService extends BaseService<
@Inject(JwtService) private readonly jwtService: JwtService,
protected readonly i18n: I18nService,
public readonly languageService: LanguageService,
@Optional() private readonly mailerService?: MailerService,
private readonly mailerService: MailerService,
) {
super(repository);
}
@ -61,7 +59,6 @@ export class InvitationService extends BaseService<
*/
async create(dto: InvitationCreateDto): Promise<Invitation> {
const jwt = await this.sign({ ...dto });
if (this.mailerService) {
try {
const defaultLanguage = await this.languageService.getDefaultLanguage();
await this.mailerService.sendMail({
@ -72,8 +69,7 @@ export class InvitationService extends BaseService<
appUrl: config.uiBaseUrl,
token: jwt,
// TODO: Which language should we use?
t: (key: string) =>
this.i18n.t(key, { lang: defaultLanguage.code }),
t: (key: string) => this.i18n.t(key, { lang: defaultLanguage.code }),
},
subject: this.i18n.t('invitation_subject'),
});
@ -86,7 +82,6 @@ export class InvitationService extends BaseService<
);
throw new InternalServerErrorException('Could not send email');
}
}
const newInvitation = await super.create({ ...dto, token: jwt });
return { ...newInvitation, token: jwt };
}

View File

@ -9,12 +9,13 @@
import { NotFoundException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { getModelToken } from '@nestjs/mongoose';
import { ISendMailOptions, MailerService } from '@nestjs-modules/mailer';
import { ISendMailOptions } from '@nestjs-modules/mailer';
import { compareSync } from 'bcryptjs';
import { Model } from 'mongoose';
import { SentMessageInfo } from 'nodemailer';
import { I18nService } from '@/i18n/services/i18n.service';
import { MailerService } from '@/mailer/mailer.service';
import { installLanguageFixtures } from '@/utils/test/fixtures/language';
import { installUserFixtures, users } from '@/utils/test/fixtures/user';
import {

View File

@ -6,15 +6,12 @@
* 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,
Injectable,
InternalServerErrorException,
NotFoundException,
Optional,
UnauthorizedException,
} from '@nestjs/common';
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
@ -24,6 +21,7 @@ import { config } from '@/config';
import { I18nService } from '@/i18n/services/i18n.service';
import { LanguageService } from '@/i18n/services/language.service';
import { LoggerService } from '@/logger/logger.service';
import { MailerService } from '@/mailer/mailer.service';
import { UserRequestResetDto, UserResetPasswordDto } from '../dto/user.dto';
@ -37,7 +35,7 @@ export class PasswordResetService {
private readonly userService: UserService,
public readonly i18n: I18nService,
public readonly languageService: LanguageService,
@Optional() private readonly mailerService?: MailerService,
private readonly mailerService: MailerService,
) {}
public readonly jwtSignOptions: JwtSignOptions = {
@ -60,7 +58,6 @@ export class PasswordResetService {
}
const jwt = await this.sign({ ...dto });
if (this.mailerService) {
try {
const defaultLanguage = await this.languageService.getDefaultLanguage();
await this.mailerService.sendMail({
@ -71,8 +68,7 @@ export class PasswordResetService {
appUrl: config.uiBaseUrl,
token: jwt,
first_name: user.first_name,
t: (key: string) =>
this.i18n.t(key, { lang: defaultLanguage.code }),
t: (key: string) => this.i18n.t(key, { lang: defaultLanguage.code }),
},
subject: this.i18n.t('password_reset_subject'),
});
@ -85,7 +81,6 @@ export class PasswordResetService {
);
throw new InternalServerErrorException('Could not send email');
}
}
// TODO: hash the token before saving it
await this.userService.updateOne({ email: dto.email }, { resetToken: jwt });

View File

@ -6,10 +6,11 @@
* 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).
*/
import { ISendMailOptions, MailerService } from '@nestjs-modules/mailer';
import { ISendMailOptions } from '@nestjs-modules/mailer';
import { SentMessageInfo } from 'nodemailer';
import { I18nService } from '@/i18n/services/i18n.service';
import { MailerService } from '@/mailer/mailer.service';
import { installLanguageFixtures } from '@/utils/test/fixtures/language';
import { installUserFixtures, users } from '@/utils/test/fixtures/user';
import {

View File

@ -6,13 +6,10 @@
* 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,
InternalServerErrorException,
Optional,
UnauthorizedException,
} from '@nestjs/common';
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
@ -21,6 +18,7 @@ import { config } from '@/config';
import { I18nService } from '@/i18n/services/i18n.service';
import { LanguageService } from '@/i18n/services/language.service';
import { LoggerService } from '@/logger/logger.service';
import { MailerService } from '@/mailer/mailer.service';
import { UserCreateDto } from '../dto/user.dto';
@ -40,7 +38,7 @@ export class ValidateAccountService {
private logger: LoggerService,
private readonly i18n: I18nService,
private readonly languageService: LanguageService,
@Optional() private readonly mailerService?: MailerService,
private readonly mailerService: MailerService,
) {}
/**
@ -77,7 +75,6 @@ export class ValidateAccountService {
) {
const confirmationToken = await this.sign({ email: dto.email });
if (this.mailerService) {
try {
const defaultLanguage = await this.languageService.getDefaultLanguage();
await this.mailerService.sendMail({
@ -88,8 +85,7 @@ export class ValidateAccountService {
appUrl: config.uiBaseUrl,
token: confirmationToken,
first_name: dto.first_name,
t: (key: string) =>
this.i18n.t(key, { lang: defaultLanguage.code }),
t: (key: string) => this.i18n.t(key, { lang: defaultLanguage.code }),
},
subject: this.i18n.t('account_confirmation_subject'),
});
@ -103,7 +99,6 @@ export class ValidateAccountService {
throw new InternalServerErrorException('Could not send email');
}
}
}
/**
* Confirms a user's account by validating the provided confirmation token.

View File

@ -12,6 +12,7 @@ import { MongooseModule } from '@nestjs/mongoose';
import { PassportModule } from '@nestjs/passport';
import { AttachmentModule } from '@/attachment/attachment.module';
import { MailerModule } from '@/mailer/mailer.module';
import { LocalAuthController } from './controllers/auth.controller';
import { ModelController } from './controllers/model.controller';
@ -46,6 +47,7 @@ import { ValidateAccountService } from './services/validate-account.service';
@Module({
imports: [
MailerModule,
MongooseModule.forFeature([
UserModel,
ModelModel,