From 3ea5df59041aec529d306a540948812a63c0b4f8 Mon Sep 17 00:00:00 2001 From: Kallesh B S Date: Wed, 2 Jul 2025 17:04:32 +0530 Subject: [PATCH] register flow --- package-lock.json | 57 ++++++- package.json | 2 + public/mail-templates/a.hbs | 14 ++ public/mail-templates/demo.hbs | 1 + src/auth/auth.controller.ts | 24 ++- src/auth/auth.dto.ts | 56 ++++++- src/auth/auth.module.ts | 54 ++++++- src/auth/auth.service.ts | 152 ++++++++++++++---- src/guards/register.guard.ts | 35 ++-- src/mail/mail.module.ts | 47 ++++-- src/oracle/oracle.module.ts | 2 +- .../user-maintenance.module.ts | 3 +- 12 files changed, 371 insertions(+), 76 deletions(-) create mode 100644 public/mail-templates/a.hbs create mode 100644 public/mail-templates/demo.hbs diff --git a/package-lock.json b/package-lock.json index d471d79..a27c0b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,8 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "cookie-parser": "^1.4.7", + "handlebars": "^4.7.8", + "joi": "^17.13.3", "jsonwebtoken": "^9.0.2", "jwk-to-pem": "^2.0.7", "mssql": "^10.0.4", @@ -1422,6 +1424,21 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -3320,6 +3337,27 @@ "url": "https://ko-fi.com/killymxi" } }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "license": "BSD-3-Clause" + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -8598,7 +8636,6 @@ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "license": "MIT", - "optional": true, "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", @@ -8620,7 +8657,6 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "license": "BSD-3-Clause", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -10351,6 +10387,19 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, "node_modules/js-beautify": { "version": "1.15.4", "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", @@ -12134,7 +12183,6 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "devOptional": true, "license": "MIT" }, "node_modules/nice-try": { @@ -16784,8 +16832,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/wrap-ansi": { "version": "6.2.0", diff --git a/package.json b/package.json index 6f214e7..74c643a 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,8 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "cookie-parser": "^1.4.7", + "handlebars": "^4.7.8", + "joi": "^17.13.3", "jsonwebtoken": "^9.0.2", "jwk-to-pem": "^2.0.7", "mssql": "^10.0.4", diff --git a/public/mail-templates/a.hbs b/public/mail-templates/a.hbs new file mode 100644 index 0000000..6867289 --- /dev/null +++ b/public/mail-templates/a.hbs @@ -0,0 +1,14 @@ +
+
+

🔐 Registration Initiated

+

A Registration has been successfully initiated for the email: {{to}}

+

Please click below to complete your registration:

+ + Register Now + +
+

Powered by

+ App Logo +
+
+
diff --git a/public/mail-templates/demo.hbs b/public/mail-templates/demo.hbs new file mode 100644 index 0000000..0228967 --- /dev/null +++ b/public/mail-templates/demo.hbs @@ -0,0 +1 @@ +

Testing....

\ No newline at end of file diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index b5eb36e..b3d6de6 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -1,14 +1,21 @@ -import { Body, Controller, Get, HttpCode, Post, Put, Req, Res, UseInterceptors } from '@nestjs/common'; +import { Body, Controller, Get, HttpCode, Post, Put, Req, Res, UseGuards, UseInterceptors } from '@nestjs/common'; import { AuthService } from './auth.service'; import { ApiTags } from '@nestjs/swagger'; -import { AuthLoginDTO } from './auth.dto'; +import { AuthLoginDTO, SendMailDTO } from './auth.dto'; import { Request, Response } from 'express'; import { LogoutInterceptor } from 'src/interceptors/logout.interceptor'; +import { RegisterGuard } from 'src/guards/register.guard'; +import { UserMaintenanceService } from 'src/oracle/user-maintenance/user-maintenance.service'; +import { RolesGuard } from 'src/guards/roles.guard'; +import { Roles } from 'src/decorators/roles.decorator'; @ApiTags('Auth') @Controller() export class AuthController { - constructor(private readonly authService: AuthService) { } + constructor( + private readonly authService: AuthService, + private readonly userMaintenanceService: UserMaintenanceService + ) { } @Post('login') @HttpCode(200) @@ -36,7 +43,7 @@ export class AuthController { return { statusCode: 200, message: "Logged-In Successfully", email: k.email } } - // @UseGuards(RegisterGuard) + @UseGuards(RegisterGuard) @Post('register') async register(@Body() body: AuthLoginDTO, @Req() req: Request) { return this.authService.registerUser(body, req); @@ -87,6 +94,15 @@ export class AuthController { return { statusCode: 200, message: "Tokens refreshed successfully" } } + @UseGuards(RolesGuard) + @Roles('sa') + @Post('sendmail') + @HttpCode(200) + async sendMail(@Body() body: SendMailDTO) { + // let token = await this.jwtService.generateToken({ email: mail }) + return this.authService.sendMail(body); + } + // @Get('checkRoles') // @UseGuards(JwtAuthGuard, RolesGuard) // @Roles('ca') diff --git a/src/auth/auth.dto.ts b/src/auth/auth.dto.ts index b5e2dd1..0946576 100644 --- a/src/auth/auth.dto.ts +++ b/src/auth/auth.dto.ts @@ -1,5 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsString } from 'class-validator'; +import { Transform, Type } from 'class-transformer'; +import { IsEmail, IsEnum, IsString, ValidateNested } from 'class-validator'; export class AuthLoginDTO { @ApiProperty({ required: true }) @@ -9,4 +10,57 @@ export class AuthLoginDTO { @ApiProperty({ required: true }) @IsString() P_PASSWORD: string; +} + +export enum MailTypeDTO { + REGISTER_CLIENT = "REGISTER_CLIENT", + REGISTER_SP = "REGISTER_SP", + REGISTER_USCIB = "REGISTER_USCIB", + FORGOT_PASSWORD = "FORGOT_PASSWORD" + // DEMO_MAIL = "DEMO_MAIL" +} + +export class SendMailDTO { + @ApiProperty({ required: true, example: 'user@example.com' }) + @IsEmail({}, { message: 'P_TO must be a valid email address' }) + P_TO: string; + + @ApiProperty({ enum: MailTypeDTO, required: true }) + @Transform(({ value }) => value?.toUpperCase()) + @IsEnum(MailTypeDTO, { message: "Invalid P_MAIL_TYPE" }) + P_MAIL_TYPE: MailTypeDTO; +} + +export class MailContextDTO { + @ApiProperty({ required: true }) + @IsString() + to: string; + + @ApiProperty({ required: true }) + @IsString() + url: string; + +} + +export class MailTemplateDTO { + @ApiProperty({ required: true }) + @IsString() + to: string; + + @ApiProperty({ required: true }) + @IsString() + subject: string; + + // @ApiProperty({ required: true }) + // @IsString() + // text: string; + + @ApiProperty({ required: true }) + @IsString() + template: string; + + @ApiProperty({ type: () => MailContextDTO, required: true }) + @ValidateNested() + @Type(() => MailContextDTO) + context: MailContextDTO; } \ No newline at end of file diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index 8e60c3f..4797f19 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -1,11 +1,59 @@ -import { Module } from '@nestjs/common'; +import { forwardRef, Module } from '@nestjs/common'; import { AuthService } from './auth.service'; import { AuthController } from './auth.controller'; import { DbModule } from 'src/db/db.module'; +import { ConfigService } from '@nestjs/config'; +import { JwtService } from '@nestjs/jwt'; +import { OracleModule } from 'src/oracle/oracle.module'; @Module({ - imports: [DbModule], - providers: [AuthService], + imports: [DbModule, forwardRef(() => OracleModule)], + providers: [ + AuthService, + { + provide: "REGISTER_SIGN_JWT", + useFactory: (config: ConfigService) => { + const base64Key = config.get('JWT_REGISTER_PRIVATE_KEY'); + if (!base64Key) { + throw new Error('JWT_REGISTER_PRIVATE_KEY is not defined in the config'); + } + const secretKey = Buffer.from(base64Key, 'base64').toString('utf-8'); + + const expiry = config.get('JWT_REGISTER_EXPIRY'); + + if (!expiry) { + throw new Error('JWT_REGISTER_PRIVATE_KEY is not defined in the config'); + } + + return new JwtService({ + secret: secretKey, + signOptions: { + expiresIn: expiry, + algorithm: 'ES384' + }, + }) + }, + inject: [ConfigService], + }, + { + provide: "REGISTER_VERIFY_JWT", + useFactory: (config: ConfigService) => { + const base64Key = config.get('JWT_REGISTER_PUBLIC_KEY'); + if (!base64Key) { + throw new Error('JWT_REGISTER_PRIVATE_KEY is not defined in the config'); + } + const secretKey = Buffer.from(base64Key, 'base64').toString('utf-8'); + + return new JwtService({ + secret: secretKey, + verifyOptions: { + algorithms: ['ES384'] + } + }) + }, + inject: [ConfigService], + } + ], controllers: [AuthController], exports: [AuthService] }) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 9e5a48b..683b45d 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -1,6 +1,6 @@ -import { Injectable, InternalServerErrorException } from '@nestjs/common'; +import { Inject, Injectable, InternalServerErrorException } from '@nestjs/common'; import { OracleDBService } from 'src/db/db.service'; -import { AuthLoginDTO } from './auth.dto'; +import { AuthLoginDTO, MailTemplateDTO, MailTypeDTO, SendMailDTO } from './auth.dto'; import * as oracledb from 'oracledb'; import { ConfigService } from '@nestjs/config'; import axios, { AxiosRequestConfig } from 'axios'; @@ -10,6 +10,9 @@ import * as jwt from "jsonwebtoken" import { UnauthorizedException } from 'src/exceptions/unauthorized.exception'; import { ConflictException } from 'src/exceptions/conflict.exception'; import { BadRequestException } from 'src/exceptions/badRequest.exception'; +import { MailerService } from '@nestjs-modules/mailer'; +import { JwtService } from '@nestjs/jwt'; +import { UserMaintenanceService } from 'src/oracle/user-maintenance/user-maintenance.service'; interface DecodedToken { email: string; @@ -33,11 +36,25 @@ export class AuthService { private readonly CLIENT_SECRET: string; private readonly TOKEN_URL: string; private readonly USERS_URL: string; + private readonly JWT_REGISTER_PRIVATE_KEY: string; + private readonly JWT_REGISTER_PUBLIC_KEY: string; constructor( private readonly oracleDBService: OracleDBService, private readonly configService: ConfigService, + private readonly mailService: MailerService, + private readonly userMaintenanceService: UserMaintenanceService, + @Inject("REGISTER_SIGN_JWT") readonly REGISTER_SIGN_JWT: JwtService, + @Inject("REGISTER_VERIFY_JWT") readonly REGISTER_VERIFY_JWT: JwtService ) { + const JWT_REGISTER_PRIVATE_KEY = this.configService.get('JWT_REGISTER_PRIVATE_KEY'); + if (!JWT_REGISTER_PRIVATE_KEY) throw new Error('Environment variable JWT_REGISTER_PRIVATE_KEY is not set'); + this.JWT_REGISTER_PRIVATE_KEY = Buffer.from(JWT_REGISTER_PRIVATE_KEY, 'base64').toString('utf-8'); + + const JWT_REGISTER_PUBLIC_KEY = this.configService.get('JWT_REGISTER_PUBLIC_KEY'); + if (!JWT_REGISTER_PUBLIC_KEY) throw new Error('Environment variable JWT_REGISTER_PRIVATE_KEY is not set'); + this.JWT_REGISTER_PUBLIC_KEY = Buffer.from(JWT_REGISTER_PUBLIC_KEY, 'base64').toString('utf-8'); + const KEYCLOAK_URL = this.configService.get('KEYCLOAK_URL'); if (!KEYCLOAK_URL) throw new Error('Environment variable KEYCLOAK_URL is not set'); this.KEYCLOAK_URL = KEYCLOAK_URL; @@ -190,7 +207,7 @@ export class AuthService { } } - async getUserSession(email:string) { + async getUserSession(email: string) { try { const uid = await this.getUserIdByEmail(email); @@ -258,7 +275,7 @@ export class AuthService { if (access_token && refresh_token && userSession.length > 0) { let tokens = await this.getTokenFromRefreshToken(req); const decoded = jwt.decode(access_token, { complete: true }); - const email:any = (decoded && typeof decoded === 'object') ? (decoded as any).payload?.email : undefined; + const email: any = (decoded && typeof decoded === 'object') ? (decoded as any).payload?.email : undefined; return { ...tokens, email } } @@ -323,25 +340,6 @@ export class AuthService { password: `${'A1!bcdef'}`, }); - let userID = await this.getUserIdByEmail(""); - - let resetPasswordURL = `${this.USERS_URL}/${userID}/reset-password`; - - let adminAccessToken = await this.getAdminAccessToken(); - - const options1: AxiosRequestConfig = { - method: 'PUT', - url: resetPasswordURL, - headers: { - Authorization: `Bearer ${adminAccessToken}` - }, - data: { - type: 'password', - value: 'A1!bcdef', - temporary: false, - } - }; - const options2: AxiosRequestConfig = { method: 'POST', url: this.TOKEN_URL, @@ -427,21 +425,45 @@ export class AuthService { const uid = await this.getUserIdByEmail(body.P_EMAILADDR); - const res = await this.assignRoleToUser(uid, "ca"); + let ROLE = ""; + + const response: any = await this.userMaintenanceService.ValidateEmail({ P_EMAILADDR: body.P_EMAILADDR }) + + + if (Array.isArray(response) && !response[0].ERRORMESG) { + switch (response[0].SOURCE) { + case "USCIB": ROLE = 'ua'; break; + case "SP": ROLE = 'sa'; break; + case "CLIENT": ROLE = 'ca'; break; + } + } + else if (Array.isArray(response) && response[0].ERRORMESG) { + throw new BadRequestException(); + } + + if (!ROLE) { + console.log("ERROR while giving access : "); + throw new InternalServerErrorException(); + } + + const res = await this.assignRoleToUser(uid, ROLE); console.log(res); - return { message: 'User created successfully' }; + return { statusCode: 201, message: 'User registered successfully' }; } } catch (error) { - // handle errors like user exists, validation errors, etc. console.log(error.message); if (error.message === "Request failed with status code 409") { throw new ConflictException("User already exist"); } - + else if (error instanceof BadRequestException) { + console.log("ERROR while registering : ", error.message); + return error; + } + console.log("ERROR while giving access : ", error.message); throw new InternalServerErrorException('Failed to create user'); } } @@ -601,4 +623,80 @@ export class AuthService { throw new UnauthorizedException('Authentication failed'); } } + + async getMailTemplate(body: SendMailDTO, token: string, source: string): Promise { + + switch (body.P_MAIL_TYPE) { + case MailTypeDTO.REGISTER_CLIENT: + return { + to: body.P_TO, + subject: `🔐 Register Your Account`, + template: 'a', + context: { + to: body.P_TO, + url: `https://client.alphaomegainfosys.com/register/${token}` + } + } + case MailTypeDTO.FORGOT_PASSWORD: + return { + to: body.P_TO, + subject: `🔐 Register Your Account`, + template: 'a', + context: { + to: body.P_TO, + url: `https://client.alphaomegainfosys.com/register/${token}` + } + } + + // case MailTypeDTO.DEMO_MAIL: + // return { + // to: body.P_TO, + // subject: `Testing`, + // template: 'demo', + // context: { + // to: body.P_TO, + // url: `https://client.alphaomegainfosys.com/register/${token}` + // } + // } + default: + return null; + } + } + + async sendMail(body: SendMailDTO) { + console.log("mail body : ", body); + let token: string; + let ROLE: string = ""; + + try { + token = this.REGISTER_SIGN_JWT.sign({ email: body.P_TO }); + console.log("Register token : ", token); + + if (!token) { + console.log("Register Token Generation failed"); + throw new InternalServerErrorException(); + } + } + catch (error) { + console.log(error.message); + + throw new InternalServerErrorException("Register Token Generation failed") + } + + const mailTemplate: MailTemplateDTO | null = await this.getMailTemplate(body, token, ROLE); + + if (!mailTemplate) { + throw new BadRequestException(); + } + + try { + await this.mailService.sendMail({ ...mailTemplate }); + console.log('Email sent successfully'); + return { statusCode: 200, message: 'Email sent successfully' }; + } catch (error) { + console.error('Failed to send email:', error.message, error); + return { statusCode: 500, message: 'Failed to send email' }; + } + } + } diff --git a/src/guards/register.guard.ts b/src/guards/register.guard.ts index a0dffc2..3b762be 100644 --- a/src/guards/register.guard.ts +++ b/src/guards/register.guard.ts @@ -2,6 +2,7 @@ import { CanActivate, ExecutionContext, + Inject, Injectable, UnauthorizedException, } from '@nestjs/common'; @@ -10,44 +11,28 @@ import { Request } from 'express'; @Injectable() export class RegisterGuard implements CanActivate { - constructor(private readonly jwtService: JwtService) {} + constructor( + @Inject("REGISTER_VERIFY_JWT") readonly REGISTER_VERIFY_JWT: JwtService + ) { } canActivate(context: ExecutionContext): boolean { const request = context.switchToHttp().getRequest(); const authHeader = request.headers['authorization']; if (!authHeader || !authHeader.startsWith('Bearer ')) { - throw new UnauthorizedException('Authorization header missing or malformed'); + throw new UnauthorizedException('Invalid Request'); } const token = authHeader.split(' ')[1]; try { - // Decode without verifying to check expiry - const decoded: any = this.jwtService.decode(token); - - console.log(decoded); - - - if (!decoded || !decoded.exp) { - throw new UnauthorizedException('Invalid token payload'); - } - - // Check expiration (exp is in seconds, Date.now() in ms) - const now = Math.floor(Date.now() / 1000); - if (decoded.exp < now) { - throw new UnauthorizedException('Token expired'); - } - - // Verify the token (checks signature and expiration again) - this.jwtService.verify(token, {secret:'yourSecretKey'}); - - // Optionally attach user info to request object for further use - request['user'] = decoded; - + const payload = this.REGISTER_VERIFY_JWT.verify(token); + request['user'] = payload; return true; } catch (err) { - throw new UnauthorizedException('Invalid or expired token'); + console.log("Error in register guard : ", err.message); + + throw new UnauthorizedException("Invalid Register Token"); } } } diff --git a/src/mail/mail.module.ts b/src/mail/mail.module.ts index 7f554dd..f763726 100644 --- a/src/mail/mail.module.ts +++ b/src/mail/mail.module.ts @@ -1,18 +1,47 @@ import { MailerModule } from '@nestjs-modules/mailer'; import { Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { join } from 'path'; +import * as Joi from 'joi'; +import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter'; @Module({ imports: [ - MailerModule.forRoot({ - transport: { - host: 'localhost', - port: 1025, - ignoreTLS: true, - }, - defaults: { - from: '"No Reply" ', - }, + ConfigModule.forRoot({ + isGlobal: true, + validationSchema: Joi.object({ + MAIL_USER: Joi.string().required(), + MAIL_PASS: Joi.string().required(), + }), + }), + + MailerModule.forRootAsync({ + imports: [ConfigModule], + useFactory: (configService: ConfigService) => ({ + transport: { + host: 'smtp.gmail.com', + port: 587, + // ignoreTLS: true, + secure: false, + auth: { + user: configService.get('MAIL_USER'), + pass: configService.get('MAIL_PASS'), + }, + }, + defaults: { + from: `"Carnet APP" <${configService.get('MAIL_USER')}>`, + }, + template: { + dir: join(__dirname, '../../..', 'public', 'mail-templates'), + adapter: new HandlebarsAdapter(), // import from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter' + options: { + strict: true, + }, + }, + }), + inject: [ConfigService], }), ], }) + export class MailModule { } diff --git a/src/oracle/oracle.module.ts b/src/oracle/oracle.module.ts index 5a6d034..d325bdc 100644 --- a/src/oracle/oracle.module.ts +++ b/src/oracle/oracle.module.ts @@ -26,6 +26,6 @@ import { AuthModule } from 'src/auth/auth.module'; ], providers: [OracleService], controllers: [], - exports: [OracleService], + exports: [OracleService, UserMaintenanceModule], }) export class OracleModule {} diff --git a/src/oracle/user-maintenance/user-maintenance.module.ts b/src/oracle/user-maintenance/user-maintenance.module.ts index 798d15e..11db21b 100644 --- a/src/oracle/user-maintenance/user-maintenance.module.ts +++ b/src/oracle/user-maintenance/user-maintenance.module.ts @@ -6,6 +6,7 @@ import { AuthModule } from 'src/auth/auth.module'; @Module({ imports: [AuthModule], controllers: [UserMaintenanceController], - providers: [UserMaintenanceService] + providers: [UserMaintenanceService], + exports: [UserMaintenanceService] }) export class UserMaintenanceModule { }