mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-14 20:00:33 +00:00
feat(nestjs): migrate to NestJS
This commit is contained in:
28
packages/server/src/modules/Auth/Auth.constants.ts
Normal file
28
packages/server/src/modules/Auth/Auth.constants.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export const jwtConstants = {
|
||||
secret:
|
||||
'DO NOT USE THIS VALUE. INSTEAD, CREATE A COMPLEX SECRET AND KEEP IT SAFE OUTSIDE OF THE SOURCE CODE.',
|
||||
};
|
||||
|
||||
export const ERRORS = {
|
||||
INVALID_DETAILS: 'INVALID_DETAILS',
|
||||
USER_INACTIVE: 'USER_INACTIVE',
|
||||
EMAIL_NOT_FOUND: 'EMAIL_NOT_FOUND',
|
||||
TOKEN_INVALID: 'TOKEN_INVALID',
|
||||
USER_NOT_FOUND: 'USER_NOT_FOUND',
|
||||
TOKEN_EXPIRED: 'TOKEN_EXPIRED',
|
||||
PHONE_NUMBER_EXISTS: 'PHONE_NUMBER_EXISTS',
|
||||
EMAIL_EXISTS: 'EMAIL_EXISTS',
|
||||
SIGNUP_RESTRICTED_NOT_ALLOWED: 'SIGNUP_RESTRICTED_NOT_ALLOWED',
|
||||
SIGNUP_RESTRICTED: 'SIGNUP_RESTRICTED',
|
||||
SIGNUP_CONFIRM_TOKEN_INVALID: 'SIGNUP_CONFIRM_TOKEN_INVALID',
|
||||
USER_ALREADY_VERIFIED: 'USER_ALREADY_VERIFIED',
|
||||
};
|
||||
|
||||
export const IS_PUBLIC_ROUTE = 'isPublic';
|
||||
|
||||
export const SendResetPasswordMailQueue = 'SendResetPasswordMailQueue';
|
||||
export const SendResetPasswordMailJob = 'SendResetPasswordMailJob';
|
||||
|
||||
export const SendSignupVerificationMailQueue =
|
||||
'SendSignupVerificationMailQueue';
|
||||
export const SendSignupVerificationMailJob = 'SendSignupVerificationMailJob';
|
||||
96
packages/server/src/modules/Auth/Auth.controller.ts
Normal file
96
packages/server/src/modules/Auth/Auth.controller.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
// @ts-nocheck
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Request,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiBody, ApiParam } from '@nestjs/swagger';
|
||||
import { JwtAuthGuard, PublicRoute } from './guards/jwt.guard';
|
||||
import { AuthenticationApplication } from './AuthApplication.sevice';
|
||||
import { AuthSignupDto } from './dtos/AuthSignup.dto';
|
||||
import { AuthSigninDto } from './dtos/AuthSignin.dto';
|
||||
import { LocalAuthGuard } from './guards/local.guard';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { AuthSigninService } from './commands/AuthSignin.service';
|
||||
|
||||
@Controller('/auth')
|
||||
@ApiTags('Auth')
|
||||
@PublicRoute()
|
||||
export class AuthController {
|
||||
constructor(
|
||||
private readonly authApp: AuthenticationApplication,
|
||||
private readonly authSignin: AuthSigninService,
|
||||
) {}
|
||||
|
||||
@Post('/signin')
|
||||
@UseGuards(LocalAuthGuard)
|
||||
@ApiOperation({ summary: 'Sign in a user' })
|
||||
@ApiBody({ type: AuthSigninDto })
|
||||
signin(@Request() req: Request, @Body() signinDto: AuthSigninDto) {
|
||||
const { user } = req;
|
||||
return { access_token: this.authSignin.signToken(user) };
|
||||
}
|
||||
|
||||
@Post('/signup')
|
||||
@ApiOperation({ summary: 'Sign up a new user' })
|
||||
@ApiBody({ type: AuthSignupDto })
|
||||
signup(@Request() req: Request, @Body() signupDto: AuthSignupDto) {
|
||||
return this.authApp.signUp(signupDto);
|
||||
}
|
||||
|
||||
@Post('/signup/confirm')
|
||||
@ApiOperation({ summary: 'Confirm user signup' })
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
email: { type: 'string', example: 'user@example.com' },
|
||||
token: { type: 'string', example: 'confirmation-token' },
|
||||
},
|
||||
},
|
||||
})
|
||||
signupConfirm(@Body('email') email: string, @Body('token') token: string) {
|
||||
return this.authApp.signUpConfirm(email, token);
|
||||
}
|
||||
|
||||
@Post('/send_reset_password')
|
||||
@ApiOperation({ summary: 'Send reset password email' })
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
email: { type: 'string', example: 'user@example.com' },
|
||||
},
|
||||
},
|
||||
})
|
||||
sendResetPassword(@Body('email') email: string) {
|
||||
return this.authApp.sendResetPassword(email);
|
||||
}
|
||||
|
||||
@Post('/reset_password/:token')
|
||||
@ApiOperation({ summary: 'Reset password using token' })
|
||||
@ApiParam({ name: 'token', description: 'Reset password token' })
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
password: { type: 'string', example: 'new-password' },
|
||||
},
|
||||
},
|
||||
})
|
||||
resetPassword(
|
||||
@Param('token') token: string,
|
||||
@Body('password') password: string,
|
||||
) {
|
||||
return this.authApp.resetPassword(token, password);
|
||||
}
|
||||
|
||||
@Get('/meta')
|
||||
meta() {
|
||||
return this.authApp.getAuthMeta();
|
||||
}
|
||||
}
|
||||
74
packages/server/src/modules/Auth/Auth.interfaces.ts
Normal file
74
packages/server/src/modules/Auth/Auth.interfaces.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { ModelObject } from 'objection';
|
||||
import { SystemUser } from '../System/models/SystemUser';
|
||||
import { TenantModel } from '../System/models/TenantModel';
|
||||
import { AuthSignupDto } from './dtos/AuthSignup.dto';
|
||||
|
||||
export interface JwtPayload {
|
||||
sub: string;
|
||||
iat: number;
|
||||
exp: number;
|
||||
}
|
||||
|
||||
export interface IAuthSignedInEventPayload {}
|
||||
export interface IAuthSigningInEventPayload {}
|
||||
export interface IAuthSignInPOJO {}
|
||||
|
||||
export interface IAuthSigningInEventPayload {
|
||||
email: string;
|
||||
password: string;
|
||||
user: ModelObject<SystemUser>;
|
||||
}
|
||||
|
||||
export interface IAuthSignedInEventPayload {
|
||||
email: string;
|
||||
password: string;
|
||||
user: ModelObject<SystemUser>;
|
||||
}
|
||||
|
||||
export interface IAuthSigningUpEventPayload {
|
||||
signupDTO: AuthSignupDto;
|
||||
}
|
||||
|
||||
export interface IAuthSignedUpEventPayload {
|
||||
signupDTO: AuthSignupDto;
|
||||
tenant: TenantModel;
|
||||
user: SystemUser;
|
||||
}
|
||||
|
||||
export interface IAuthSignInPOJO {
|
||||
user: ModelObject<SystemUser>;
|
||||
token: string;
|
||||
tenant: ModelObject<TenantModel>;
|
||||
}
|
||||
|
||||
export interface IAuthResetedPasswordEventPayload {
|
||||
user: SystemUser;
|
||||
token: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface IAuthSendingResetPassword {
|
||||
user: SystemUser;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface IAuthSendedResetPassword {
|
||||
user: SystemUser;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface IAuthGetMetaPOJO {
|
||||
signupDisabled: boolean;
|
||||
}
|
||||
|
||||
export interface IAuthSignUpVerifingEventPayload {
|
||||
email: string;
|
||||
verifyToken: string;
|
||||
userId: number;
|
||||
}
|
||||
|
||||
export interface IAuthSignUpVerifiedEventPayload {
|
||||
email: string;
|
||||
verifyToken: string;
|
||||
userId: number;
|
||||
}
|
||||
75
packages/server/src/modules/Auth/Auth.module.ts
Normal file
75
packages/server/src/modules/Auth/Auth.module.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AuthController } from './Auth.controller';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { JwtStrategy } from './strategies/Jwt.strategy';
|
||||
import { AuthenticationApplication } from './AuthApplication.sevice';
|
||||
import { AuthSendResetPasswordService } from './commands/AuthSendResetPassword.service';
|
||||
import { AuthResetPasswordService } from './commands/AuthResetPassword.service';
|
||||
import { AuthSignupConfirmResendService } from './commands/AuthSignupConfirmResend.service';
|
||||
import { AuthSignupConfirmService } from './commands/AuthSignupConfirm.service';
|
||||
import { AuthSignupService } from './commands/AuthSignup.service';
|
||||
import { AuthSigninService } from './commands/AuthSignin.service';
|
||||
import { PasswordReset } from './models/PasswordReset';
|
||||
import { TenantDBManagerModule } from '../TenantDBManager/TenantDBManager.module';
|
||||
import { AuthenticationMailMesssages } from './AuthMailMessages.esrvice';
|
||||
import { LocalStrategy } from './strategies/Local.strategy';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import { JwtAuthGuard } from './guards/jwt.guard';
|
||||
import { AuthMailSubscriber } from './Subscribers/AuthMail.subscriber';
|
||||
import { BullModule } from '@nestjs/bullmq';
|
||||
import {
|
||||
SendResetPasswordMailQueue,
|
||||
SendSignupVerificationMailQueue,
|
||||
} from './Auth.constants';
|
||||
import { SendResetPasswordMailProcessor } from './processors/SendResetPasswordMail.processor';
|
||||
import { SendSignupVerificationMailProcessor } from './processors/SendSignupVerificationMail.processor';
|
||||
import { MailModule } from '../Mail/Mail.module';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { InjectSystemModel } from '../System/SystemModels/SystemModels.module';
|
||||
import { GetAuthMetaService } from './queries/GetAuthMeta.service';
|
||||
|
||||
const models = [InjectSystemModel(PasswordReset)];
|
||||
|
||||
@Module({
|
||||
controllers: [AuthController],
|
||||
imports: [
|
||||
MailModule,
|
||||
PassportModule.register({ defaultStrategy: 'jwt' }),
|
||||
JwtModule.registerAsync({
|
||||
inject: [ConfigService],
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
secret: configService.get('jwt.secret'),
|
||||
signOptions: { expiresIn: '1d', algorithm: 'HS384' },
|
||||
verifyOptions: { algorithms: ['HS384'] },
|
||||
}),
|
||||
}),
|
||||
TenantDBManagerModule,
|
||||
BullModule.registerQueue({ name: SendResetPasswordMailQueue }),
|
||||
BullModule.registerQueue({ name: SendSignupVerificationMailQueue }),
|
||||
|
||||
],
|
||||
exports: [...models],
|
||||
providers: [
|
||||
...models,
|
||||
LocalStrategy,
|
||||
JwtStrategy,
|
||||
AuthenticationApplication,
|
||||
AuthSendResetPasswordService,
|
||||
AuthResetPasswordService,
|
||||
AuthSignupConfirmResendService,
|
||||
AuthSignupConfirmService,
|
||||
AuthSignupService,
|
||||
AuthSigninService,
|
||||
AuthenticationMailMesssages,
|
||||
SendResetPasswordMailProcessor,
|
||||
SendSignupVerificationMailProcessor,
|
||||
GetAuthMetaService,
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: JwtAuthGuard,
|
||||
},
|
||||
AuthMailSubscriber,
|
||||
],
|
||||
})
|
||||
export class AuthModule {}
|
||||
10
packages/server/src/modules/Auth/Auth.utils.ts
Normal file
10
packages/server/src/modules/Auth/Auth.utils.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import * as bcrypt from 'bcrypt';
|
||||
|
||||
export const hashPassword = (password: string): Promise<string> =>
|
||||
new Promise((resolve) => {
|
||||
bcrypt.genSalt(10, (error, salt) => {
|
||||
bcrypt.hash(password, salt, (err, hash: string) => {
|
||||
resolve(hash);
|
||||
});
|
||||
});
|
||||
});
|
||||
86
packages/server/src/modules/Auth/AuthApplication.sevice.ts
Normal file
86
packages/server/src/modules/Auth/AuthApplication.sevice.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { AuthSigninService } from './commands/AuthSignin.service';
|
||||
import { AuthSignupService } from './commands/AuthSignup.service';
|
||||
import { AuthSignupConfirmService } from './commands/AuthSignupConfirm.service';
|
||||
import { AuthSignupConfirmResendService } from './commands/AuthSignupConfirmResend.service';
|
||||
import { AuthSigninDto } from './dtos/AuthSignin.dto';
|
||||
import { AuthSignupDto } from './dtos/AuthSignup.dto';
|
||||
import { AuthSendResetPasswordService } from './commands/AuthSendResetPassword.service';
|
||||
import { AuthResetPasswordService } from './commands/AuthResetPassword.service';
|
||||
import { GetAuthMetaService } from './queries/GetAuthMeta.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthenticationApplication {
|
||||
constructor(
|
||||
private readonly authSigninService: AuthSigninService,
|
||||
private readonly authSignupService: AuthSignupService,
|
||||
private readonly authSignupConfirmService: AuthSignupConfirmService,
|
||||
private readonly authSignUpConfirmResendService: AuthSignupConfirmResendService,
|
||||
private readonly authResetPasswordService: AuthResetPasswordService,
|
||||
private readonly authSendResetPasswordService: AuthSendResetPasswordService,
|
||||
private readonly authGetMeta: GetAuthMetaService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Signin and generates JWT token.
|
||||
* @param {string} email - Email address.
|
||||
* @param {string} password - Password.
|
||||
*/
|
||||
public async signIn(email: string, password: string) {
|
||||
return this.authSigninService.signin(email, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signup a new user.
|
||||
* @param {IRegisterDTO} signupDTO
|
||||
*/
|
||||
public async signUp(signupDto: AuthSignupDto) {
|
||||
return this.authSignupService.signUp(signupDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifying the provided user's email after signin-up.
|
||||
* @param {string} email - User email.
|
||||
* @param {string} token - Verification token.
|
||||
* @returns {Promise<SystemUser>}
|
||||
*/
|
||||
public async signUpConfirm(email: string, token: string) {
|
||||
return this.authSignupConfirmService.signupConfirm(email, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-sends the confirmation email of the given system user.
|
||||
* @param {number} userId - System user id.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async signUpConfirmResend(userId: number) {
|
||||
return this.authSignUpConfirmResendService.signUpConfirmResend(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and retrieve password reset token for the given user email.
|
||||
* @param {string} email
|
||||
* @return {<Promise<IPasswordReset>}
|
||||
*/
|
||||
public async sendResetPassword(email: string) {
|
||||
return this.authSendResetPasswordService.sendResetPassword(email);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets a user password from given token.
|
||||
* @param {string} token - Password reset token.
|
||||
* @param {string} password - New Password.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async resetPassword(token: string, password: string) {
|
||||
return this.authResetPasswordService.resetPassword(token, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the authentication meta for SPA.
|
||||
* @returns {Promise<IAuthGetMetaPOJO>}
|
||||
*/
|
||||
public async getAuthMeta() {
|
||||
return this.authGetMeta.getAuthMeta();
|
||||
}
|
||||
}
|
||||
82
packages/server/src/modules/Auth/AuthMailMessages.esrvice.ts
Normal file
82
packages/server/src/modules/Auth/AuthMailMessages.esrvice.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import * as path from 'path';
|
||||
import { SystemUser } from '../System/models/SystemUser';
|
||||
import { ModelObject } from 'objection';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Mail } from '../Mail/Mail';
|
||||
import { MailTransporter } from '../Mail/MailTransporter.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthenticationMailMesssages {
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
private readonly mailTransporter: MailTransporter,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Sends reset password message.
|
||||
* @param {ISystemUser} user - The system user.
|
||||
* @param {string} token - Reset password token.
|
||||
* @returns {Mail}
|
||||
*/
|
||||
resetPasswordMessage(user: ModelObject<SystemUser>, token: string) {
|
||||
const baseURL = this.configService.get('baseURL');
|
||||
|
||||
return new Mail()
|
||||
.setSubject('Bigcapital - Password Reset')
|
||||
.setView('mail/ResetPassword.html')
|
||||
.setTo(user.email)
|
||||
.setAttachments([
|
||||
{
|
||||
filename: 'bigcapital.png',
|
||||
path: path.join(global.__static_dirname, `/images/bigcapital.png`),
|
||||
cid: 'bigcapital_logo',
|
||||
},
|
||||
])
|
||||
.setData({
|
||||
resetPasswordUrl: `${baseURL}/auth/reset_password/${token}`,
|
||||
first_name: user.firstName,
|
||||
last_name: user.lastName,
|
||||
});
|
||||
}
|
||||
|
||||
sendResetPasswordMail(user: ModelObject<SystemUser>, token: string) {
|
||||
const mail = this.resetPasswordMessage(user, token);
|
||||
|
||||
return this.mailTransporter.send(mail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends signup verification mail.
|
||||
* @param {string} email - Email address
|
||||
* @param {string} fullName - User name.
|
||||
* @param {string} token - Verification token.
|
||||
* @returns {Mail}
|
||||
*/
|
||||
signupVerificationMail(email: string, fullName: string, token: string) {
|
||||
const baseURL = this.configService.get('baseURL');
|
||||
const verifyUrl = `${baseURL}/auth/email_confirmation?token=${token}&email=${email}`;
|
||||
|
||||
return new Mail()
|
||||
.setSubject('Bigcapital - Verify your email')
|
||||
.setView('mail/SignupVerifyEmail.html')
|
||||
.setTo(email)
|
||||
.setAttachments([
|
||||
{
|
||||
filename: 'bigcapital.png',
|
||||
path: path.join(global.__static_dirname, `/images/bigcapital.png`),
|
||||
cid: 'bigcapital_logo',
|
||||
},
|
||||
])
|
||||
.setData({ verifyUrl, fullName });
|
||||
}
|
||||
|
||||
sendSignupVerificationMail(email: string, fullName: string, token: string) {
|
||||
const mail = this.signupVerificationMail(
|
||||
email,
|
||||
fullName,
|
||||
token,
|
||||
);
|
||||
return this.mailTransporter.send(mail);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { SystemUser } from '@/modules/System/models/SystemUser';
|
||||
import { PasswordReset } from '../models/PasswordReset';
|
||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||
import { ERRORS } from '../Auth.constants';
|
||||
import { hashPassword } from '../Auth.utils';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
import { IAuthResetedPasswordEventPayload } from '../Auth.interfaces';
|
||||
|
||||
@Injectable()
|
||||
export class AuthResetPasswordService {
|
||||
/**
|
||||
* @param {ConfigService} configService - Config service.
|
||||
* @param {EventEmitter2} eventEmitter - Event emitter.
|
||||
* @param {typeof SystemUser} systemUserModel
|
||||
* @param {typeof PasswordReset} passwordResetModel - Reset password model.
|
||||
*/
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
|
||||
@Inject(SystemUser.name)
|
||||
private readonly systemUserModel: typeof SystemUser,
|
||||
|
||||
@Inject(PasswordReset.name)
|
||||
private readonly passwordResetModel: typeof PasswordReset,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Resets a user password from given token.
|
||||
* @param {string} token - Password reset token.
|
||||
* @param {string} password - New Password.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async resetPassword(token: string, password: string): Promise<void> {
|
||||
// Finds the password reset token.
|
||||
const tokenModel = await this.passwordResetModel
|
||||
.query()
|
||||
.findOne('token', token);
|
||||
|
||||
// In case the password reset token not found throw token invalid error..
|
||||
if (!tokenModel) {
|
||||
throw new ServiceError(ERRORS.TOKEN_INVALID);
|
||||
}
|
||||
const resetPasswordSeconds = this.configService.get('resetPasswordSeconds');
|
||||
|
||||
// Different between tokne creation datetime and current time.
|
||||
if (moment().diff(tokenModel.createdAt, 'seconds') > resetPasswordSeconds) {
|
||||
// Deletes the expired token by expired token email.
|
||||
await this.deletePasswordResetToken(tokenModel.email);
|
||||
throw new ServiceError(ERRORS.TOKEN_EXPIRED);
|
||||
}
|
||||
const user = await this.systemUserModel
|
||||
.query()
|
||||
.findOne({ email: tokenModel.email });
|
||||
|
||||
if (!user) {
|
||||
throw new ServiceError(ERRORS.USER_NOT_FOUND);
|
||||
}
|
||||
const hashedPassword = await hashPassword(password);
|
||||
|
||||
await this.systemUserModel
|
||||
.query()
|
||||
.findById(user.id)
|
||||
.update({ password: hashedPassword });
|
||||
|
||||
// Deletes the used token.
|
||||
await this.deletePasswordResetToken(tokenModel.email);
|
||||
|
||||
// Triggers `onResetPassword` event.
|
||||
await this.eventEmitter.emitAsync(events.auth.resetPassword, {
|
||||
user,
|
||||
token,
|
||||
password,
|
||||
} as IAuthResetedPasswordEventPayload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the password reset token by the given email.
|
||||
* @param {string} email
|
||||
* @returns {Promise}
|
||||
*/
|
||||
private async deletePasswordResetToken(email: string) {
|
||||
return PasswordReset.query().where('email', email).delete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import * as uniqid from 'uniqid';
|
||||
import {
|
||||
IAuthSendedResetPassword,
|
||||
IAuthSendingResetPassword,
|
||||
} from '../Auth.interfaces';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { PasswordReset } from '../models/PasswordReset';
|
||||
import { SystemUser } from '@/modules/System/models/SystemUser';
|
||||
import { events } from '@/common/events/events';
|
||||
|
||||
@Injectable()
|
||||
export class AuthSendResetPasswordService {
|
||||
/**
|
||||
* @param {EventEmitter2} eventPublisher - Event emitter.
|
||||
* @param {typeof PasswordReset} resetPasswordModel - Password reset model.
|
||||
* @param {typeof SystemUser} systemUserModel - System user model.
|
||||
*/
|
||||
constructor(
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
|
||||
@Inject(PasswordReset.name)
|
||||
private readonly resetPasswordModel: typeof PasswordReset,
|
||||
|
||||
@Inject(SystemUser.name)
|
||||
private readonly systemUserModel: typeof SystemUser,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Sends the given email reset password email.
|
||||
* @param {string} email - Email address.
|
||||
*/
|
||||
async sendResetPassword(email: string): Promise<void> {
|
||||
const user = await this.systemUserModel
|
||||
.query()
|
||||
.findOne({ email })
|
||||
.throwIfNotFound();
|
||||
|
||||
const token: string = uniqid();
|
||||
|
||||
// Triggers sending reset password event.
|
||||
await this.eventPublisher.emitAsync(events.auth.sendingResetPassword, {
|
||||
user,
|
||||
token,
|
||||
} as IAuthSendingResetPassword);
|
||||
|
||||
// Delete all stored tokens of reset password that associate to the give email.
|
||||
this.deletePasswordResetToken(email);
|
||||
|
||||
// Creates a new password reset row with unique token.
|
||||
const passwordReset = await this.resetPasswordModel.query().insert({
|
||||
email,
|
||||
token,
|
||||
});
|
||||
// Triggers sent reset password event.
|
||||
await this.eventPublisher.emitAsync(events.auth.sendResetPassword, {
|
||||
user,
|
||||
token,
|
||||
} as IAuthSendedResetPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the password reset token by the given email.
|
||||
* @param {string} email
|
||||
* @returns {Promise}
|
||||
*/
|
||||
private async deletePasswordResetToken(email: string) {
|
||||
return this.resetPasswordModel.query().where('email', email).delete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { ClsService } from 'nestjs-cls';
|
||||
import { Inject, Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { SystemUser } from '@/modules/System/models/SystemUser';
|
||||
import { ModelObject } from 'objection';
|
||||
import { JwtPayload } from '../Auth.interfaces';
|
||||
|
||||
@Injectable()
|
||||
export class AuthSigninService {
|
||||
constructor(
|
||||
@Inject(SystemUser.name)
|
||||
private readonly systemUserModel: typeof SystemUser,
|
||||
private readonly jwtService: JwtService,
|
||||
private readonly clsService: ClsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validates the given email and password.
|
||||
* @param {string} email - Signin email address.
|
||||
* @param {string} password - Signin password.
|
||||
* @returns {Promise<ModelObject<SystemUser>>}
|
||||
*/
|
||||
async signin(
|
||||
email: string,
|
||||
password: string,
|
||||
): Promise<ModelObject<SystemUser>> {
|
||||
let user: SystemUser;
|
||||
|
||||
try {
|
||||
user = await this.systemUserModel
|
||||
.query()
|
||||
.findOne({ email })
|
||||
.throwIfNotFound();
|
||||
} catch (err) {
|
||||
throw new UnauthorizedException(
|
||||
`There isn't any user with email: ${email}`,
|
||||
);
|
||||
}
|
||||
if (!(await user.checkPassword(password))) {
|
||||
throw new UnauthorizedException(
|
||||
`Wrong password for user with email: ${email}`,
|
||||
);
|
||||
}
|
||||
if (!user.verified) {
|
||||
throw new UnauthorizedException(
|
||||
`The user is not verified yet, check out your mail inbox.`
|
||||
);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the given jwt payload.
|
||||
* @param {JwtPayload} payload
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
async verifyPayload(payload: JwtPayload): Promise<any> {
|
||||
let user: SystemUser;
|
||||
|
||||
try {
|
||||
user = await this.systemUserModel
|
||||
.query()
|
||||
.findOne({ email: payload.sub })
|
||||
.throwIfNotFound();
|
||||
|
||||
this.clsService.set('tenantId', user.tenantId);
|
||||
this.clsService.set('userId', user.id);
|
||||
} catch (error) {
|
||||
throw new UnauthorizedException(
|
||||
`There isn't any user with email: ${payload.sub}`,
|
||||
);
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {SystemUser} user
|
||||
* @returns {string}
|
||||
*/
|
||||
signToken(user: SystemUser): string {
|
||||
const payload = {
|
||||
sub: user.email,
|
||||
};
|
||||
return this.jwtService.sign(payload);
|
||||
}
|
||||
}
|
||||
130
packages/server/src/modules/Auth/commands/AuthSignup.service.ts
Normal file
130
packages/server/src/modules/Auth/commands/AuthSignup.service.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import * as crypto from 'crypto';
|
||||
import * as moment from 'moment';
|
||||
import { events } from '@/common/events/events';
|
||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||
import { SystemUser } from '@/modules/System/models/SystemUser';
|
||||
import { TenantsManagerService } from '@/modules/TenantDBManager/TenantsManager';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { isEmpty } from 'class-validator';
|
||||
import { AuthSignupDto } from '../dtos/AuthSignup.dto';
|
||||
import {
|
||||
IAuthSignedUpEventPayload,
|
||||
IAuthSigningUpEventPayload,
|
||||
} from '../Auth.interfaces';
|
||||
import { defaultTo } from 'ramda';
|
||||
import { ERRORS } from '../Auth.constants';
|
||||
import { hashPassword } from '../Auth.utils';
|
||||
|
||||
@Injectable()
|
||||
export class AuthSignupService {
|
||||
/**
|
||||
* @param {ConfigService} configService - Config service
|
||||
* @param {EventEmitter2} eventEmitter - Event emitter
|
||||
* @param {TenantsManagerService} tenantsManager - Tenants manager
|
||||
* @param {typeof SystemUser} systemUserModel - System user model
|
||||
*/
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly tenantsManager: TenantsManagerService,
|
||||
|
||||
@Inject(SystemUser.name)
|
||||
private readonly systemUserModel: typeof SystemUser,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Registers a new tenant with user from user input.
|
||||
* @param {AuthSignupDto} signupDTO
|
||||
*/
|
||||
public async signUp(signupDTO: AuthSignupDto) {
|
||||
// Validates the signup disable restrictions.
|
||||
await this.validateSignupRestrictions(signupDTO.email);
|
||||
|
||||
// Validates the given email uniqiness.
|
||||
await this.validateEmailUniqiness(signupDTO.email);
|
||||
|
||||
const hashedPassword = await hashPassword(signupDTO.password);
|
||||
const signupConfirmation = this.configService.get('signupConfirmation');
|
||||
|
||||
const verifyTokenCrypto = crypto.randomBytes(64).toString('hex');
|
||||
const verifiedEnabed = defaultTo(signupConfirmation.enabled, false);
|
||||
const verifyToken = verifiedEnabed ? verifyTokenCrypto : '';
|
||||
const verified = !verifiedEnabed;
|
||||
|
||||
const inviteAcceptedAt = moment().format('YYYY-MM-DD');
|
||||
|
||||
// Triggers signin up event.
|
||||
await this.eventEmitter.emitAsync(events.auth.signingUp, {
|
||||
signupDTO,
|
||||
} as IAuthSigningUpEventPayload);
|
||||
|
||||
const tenant = await this.tenantsManager.createTenant();
|
||||
const user = await this.systemUserModel.query().insert({
|
||||
...signupDTO,
|
||||
verifyToken,
|
||||
verified,
|
||||
active: true,
|
||||
password: hashedPassword,
|
||||
tenantId: tenant.id,
|
||||
inviteAcceptedAt,
|
||||
});
|
||||
// Triggers signed up event.
|
||||
await this.eventEmitter.emitAsync(events.auth.signUp, {
|
||||
signupDTO,
|
||||
tenant,
|
||||
user,
|
||||
} as IAuthSignedUpEventPayload);
|
||||
|
||||
return {
|
||||
userId: user.id,
|
||||
tenantId: user.tenantId,
|
||||
organizationId: tenant.organizationId,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates email uniqiness on the storage.
|
||||
* @param {string} email - Email address
|
||||
*/
|
||||
private async validateEmailUniqiness(email: string) {
|
||||
const isEmailExists = await this.systemUserModel.query().findOne({ email });
|
||||
|
||||
if (isEmailExists) {
|
||||
throw new ServiceError(ERRORS.EMAIL_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate sign-up disable restrictions.
|
||||
* @param {string} email - Signup email address
|
||||
*/
|
||||
private async validateSignupRestrictions(email: string) {
|
||||
const signupRestrictions = this.configService.get('signupRestrictions');
|
||||
|
||||
// Can't continue if the signup is not disabled.
|
||||
if (!signupRestrictions.disabled) return;
|
||||
|
||||
// Validate the allowed email addresses and domains.
|
||||
if (
|
||||
!isEmpty(signupRestrictions.allowedEmails) ||
|
||||
!isEmpty(signupRestrictions.allowedDomains)
|
||||
) {
|
||||
const emailDomain = email.split('@').pop();
|
||||
const isAllowedEmail =
|
||||
signupRestrictions.allowedEmails.indexOf(email) !== -1;
|
||||
|
||||
const isAllowedDomain = signupRestrictions.allowedDomains.some(
|
||||
(domain) => emailDomain === domain,
|
||||
);
|
||||
|
||||
if (!isAllowedEmail && !isAllowedDomain) {
|
||||
throw new ServiceError(ERRORS.SIGNUP_RESTRICTED_NOT_ALLOWED);
|
||||
}
|
||||
// Throw error if the signup is disabled with no exceptions.
|
||||
} else {
|
||||
throw new ServiceError(ERRORS.SIGNUP_RESTRICTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||
import { SystemUser } from '@/modules/System/models/SystemUser';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { ERRORS } from '../Auth.constants';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import {
|
||||
IAuthSignUpVerifiedEventPayload,
|
||||
IAuthSignUpVerifingEventPayload,
|
||||
} from '../Auth.interfaces';
|
||||
import { events } from '@/common/events/events';
|
||||
|
||||
@Injectable()
|
||||
export class AuthSignupConfirmService {
|
||||
constructor(
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
|
||||
@Inject(SystemUser.name)
|
||||
private readonly systemUserModel: typeof SystemUser,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Verifies the provided user's email after signing-up.
|
||||
* @throws {ServiceErrors}
|
||||
* @param {IRegisterDTO} signupDTO
|
||||
* @returns {Promise<ISystemUser>}
|
||||
*/
|
||||
public async signupConfirm(
|
||||
email: string,
|
||||
verifyToken: string,
|
||||
): Promise<SystemUser> {
|
||||
const foundUser = await this.systemUserModel
|
||||
.query()
|
||||
.findOne({ email, verifyToken });
|
||||
|
||||
if (!foundUser) {
|
||||
throw new ServiceError(ERRORS.SIGNUP_CONFIRM_TOKEN_INVALID);
|
||||
}
|
||||
const userId = foundUser.id;
|
||||
|
||||
// Triggers `signUpConfirming` event.
|
||||
await this.eventPublisher.emitAsync(events.auth.signUpConfirming, {
|
||||
email,
|
||||
verifyToken,
|
||||
userId,
|
||||
} as IAuthSignUpVerifingEventPayload);
|
||||
|
||||
const updatedUser = await this.systemUserModel
|
||||
.query()
|
||||
.patchAndFetchById(foundUser.id, {
|
||||
verified: true,
|
||||
verifyToken: '',
|
||||
});
|
||||
// Triggers `signUpConfirmed` event.
|
||||
await this.eventPublisher.emitAsync(events.auth.signUpConfirmed, {
|
||||
email,
|
||||
verifyToken,
|
||||
userId,
|
||||
} as IAuthSignUpVerifiedEventPayload);
|
||||
|
||||
return updatedUser as SystemUser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export class AuthSignupConfirmResendService {
|
||||
signUpConfirmResend(userId: number) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
11
packages/server/src/modules/Auth/dtos/AuthSignin.dto.ts
Normal file
11
packages/server/src/modules/Auth/dtos/AuthSignin.dto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class AuthSigninDto {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
password: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
email: string;
|
||||
}
|
||||
20
packages/server/src/modules/Auth/dtos/AuthSignup.dto.ts
Normal file
20
packages/server/src/modules/Auth/dtos/AuthSignup.dto.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class AuthSignupDto {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
firstName: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
lastName: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@IsEmail()
|
||||
email: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
password: string;
|
||||
}
|
||||
5
packages/server/src/modules/Auth/guards/Local.guard.ts
Normal file
5
packages/server/src/modules/Auth/guards/Local.guard.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Injectable, ExecutionContext } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
|
||||
@Injectable()
|
||||
export class LocalAuthGuard extends AuthGuard('local') {}
|
||||
28
packages/server/src/modules/Auth/guards/jwt.guard.ts
Normal file
28
packages/server/src/modules/Auth/guards/jwt.guard.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { ExecutionContext, Injectable, SetMetadata } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { ClsService } from 'nestjs-cls';
|
||||
import { IS_PUBLIC_ROUTE } from '../Auth.constants';
|
||||
|
||||
export const PublicRoute = () => SetMetadata(IS_PUBLIC_ROUTE, true);
|
||||
|
||||
@Injectable()
|
||||
export class JwtAuthGuard extends AuthGuard('jwt') {
|
||||
constructor(
|
||||
private reflector: Reflector,
|
||||
private readonly cls: ClsService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
canActivate(context: ExecutionContext) {
|
||||
const isPublic = this.reflector.getAllAndOverride<boolean>(
|
||||
IS_PUBLIC_ROUTE,
|
||||
[context.getHandler(), context.getClass()],
|
||||
);
|
||||
if (isPublic) {
|
||||
return true;
|
||||
}
|
||||
return super.canActivate(context);
|
||||
}
|
||||
}
|
||||
21
packages/server/src/modules/Auth/models/PasswordReset.ts
Normal file
21
packages/server/src/modules/Auth/models/PasswordReset.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { SystemModel } from '@/modules/System/models/SystemModel';
|
||||
|
||||
export class PasswordReset extends SystemModel {
|
||||
readonly email: string;
|
||||
readonly token: string;
|
||||
readonly createdAt: Date;
|
||||
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
static get tableName() {
|
||||
return 'password_resets';
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamps columns.
|
||||
*/
|
||||
get timestamps() {
|
||||
return ['createdAt'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Processor, WorkerHost } from '@nestjs/bullmq';
|
||||
import { Scope } from '@nestjs/common';
|
||||
import {
|
||||
SendResetPasswordMailJob,
|
||||
SendResetPasswordMailQueue,
|
||||
} from '../Auth.constants';
|
||||
import { Process } from '@nestjs/bull';
|
||||
import { Job } from 'bullmq';
|
||||
import { AuthenticationMailMesssages } from '../AuthMailMessages.esrvice';
|
||||
import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
|
||||
import { ModelObject } from 'objection';
|
||||
import { SystemUser } from '@/modules/System/models/SystemUser';
|
||||
|
||||
@Processor({
|
||||
name: SendResetPasswordMailQueue,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class SendResetPasswordMailProcessor extends WorkerHost {
|
||||
constructor(
|
||||
private readonly authMailMesssages: AuthenticationMailMesssages,
|
||||
private readonly mailTransporter: MailTransporter,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
@Process(SendResetPasswordMailJob)
|
||||
async process(job: Job<SendResetPasswordMailJobPayload>) {
|
||||
try {
|
||||
await this.authMailMesssages.sendResetPasswordMail(
|
||||
job.data.user,
|
||||
job.data.token,
|
||||
);
|
||||
} catch (error) {
|
||||
console.log('Error occured during send reset password mail', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface SendResetPasswordMailJobPayload {
|
||||
user: ModelObject<SystemUser>;
|
||||
token: string;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
import { Job } from 'bullmq';
|
||||
import { Process } from '@nestjs/bull';
|
||||
import { Processor, WorkerHost } from '@nestjs/bullmq';
|
||||
import {
|
||||
SendSignupVerificationMailJob,
|
||||
SendSignupVerificationMailQueue,
|
||||
} from '../Auth.constants';
|
||||
import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
|
||||
import { AuthenticationMailMesssages } from '../AuthMailMessages.esrvice';
|
||||
|
||||
@Processor({
|
||||
name: SendSignupVerificationMailQueue,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class SendSignupVerificationMailProcessor extends WorkerHost {
|
||||
constructor(
|
||||
private readonly authMailMesssages: AuthenticationMailMesssages,
|
||||
private readonly mailTransporter: MailTransporter,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
@Process(SendSignupVerificationMailJob)
|
||||
async process(job: Job<SendSignupVerificationMailJobPayload>) {
|
||||
try {
|
||||
await this.authMailMesssages.sendSignupVerificationMail(
|
||||
job.data.email,
|
||||
job.data.fullName,
|
||||
job.data.token,
|
||||
);
|
||||
} catch (error) {
|
||||
console.log('Error occured during send signup verification mail', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface SendSignupVerificationMailJobPayload {
|
||||
email: string;
|
||||
fullName: string;
|
||||
token: string;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { IAuthGetMetaPOJO } from '../Auth.interfaces';
|
||||
|
||||
@Injectable()
|
||||
export class GetAuthMetaService {
|
||||
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
) {
|
||||
|
||||
}
|
||||
/**
|
||||
* Retrieves the authentication meta for SPA.
|
||||
* @returns {Promise<IAuthGetMetaPOJO>}
|
||||
*/
|
||||
public async getAuthMeta(): Promise<IAuthGetMetaPOJO> {
|
||||
return {
|
||||
signupDisabled: this.configService.get('signupRestrictions.disabled'),
|
||||
};
|
||||
}
|
||||
}
|
||||
24
packages/server/src/modules/Auth/strategies/Jwt.strategy.ts
Normal file
24
packages/server/src/modules/Auth/strategies/Jwt.strategy.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import { AuthSigninService } from '../commands/AuthSignin.service';
|
||||
import { JwtPayload } from '../Auth.interfaces';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
constructor(
|
||||
private readonly authSigninService: AuthSigninService,
|
||||
private readonly configService: ConfigService,
|
||||
) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExpiration: false,
|
||||
secretOrKey: configService.get('jwt.secret'),
|
||||
});
|
||||
}
|
||||
|
||||
validate(payload: JwtPayload) {
|
||||
return this.authSigninService.verifyPayload(payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Strategy } from 'passport-local';
|
||||
import { AuthSigninService } from '../commands/AuthSignin.service';
|
||||
import { ModelObject } from 'objection';
|
||||
import { SystemUser } from '../../System/models/SystemUser';
|
||||
|
||||
@Injectable()
|
||||
export class LocalStrategy extends PassportStrategy(Strategy, 'local') {
|
||||
constructor(private readonly authSigninService: AuthSigninService) {
|
||||
super({
|
||||
usernameField: 'email',
|
||||
passReqToCallback: false,
|
||||
session: false,
|
||||
});
|
||||
}
|
||||
|
||||
validate(email: string, password: string): Promise<ModelObject<SystemUser>> {
|
||||
return this.authSigninService.signin(email, password);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { events } from '@/common/events/events';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import {
|
||||
IAuthSendedResetPassword,
|
||||
IAuthSignedUpEventPayload,
|
||||
} from '../Auth.interfaces';
|
||||
import { Queue } from 'bullmq';
|
||||
import { InjectQueue } from '@nestjs/bullmq';
|
||||
import { SendResetPasswordMailJobPayload } from '../processors/SendResetPasswordMail.processor';
|
||||
import {
|
||||
SendResetPasswordMailJob,
|
||||
SendResetPasswordMailQueue,
|
||||
SendSignupVerificationMailJob,
|
||||
SendSignupVerificationMailQueue,
|
||||
} from '../Auth.constants';
|
||||
import { SendSignupVerificationMailJobPayload } from '../processors/SendSignupVerificationMail.processor';
|
||||
|
||||
@Injectable()
|
||||
export class AuthMailSubscriber {
|
||||
constructor(
|
||||
@InjectQueue(SendResetPasswordMailQueue)
|
||||
private readonly sendResetPasswordMailQueue: Queue,
|
||||
|
||||
@InjectQueue(SendSignupVerificationMailQueue)
|
||||
private readonly sendSignupVerificationMailQueue: Queue,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param {IAuthSignedUpEventPayload} payload
|
||||
*/
|
||||
@OnEvent(events.auth.signUp)
|
||||
async handleSignupSendVerificationMail(payload: IAuthSignedUpEventPayload) {
|
||||
try {
|
||||
const job = await this.sendSignupVerificationMailQueue.add(
|
||||
SendSignupVerificationMailJob,
|
||||
{
|
||||
email: payload.user.email,
|
||||
fullName: payload.user.firstName,
|
||||
token: payload.user.verifyToken,
|
||||
} as SendSignupVerificationMailJobPayload,
|
||||
{
|
||||
delay: 0,
|
||||
},
|
||||
);
|
||||
console.log(job);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {IAuthSendedResetPassword} payload
|
||||
*/
|
||||
@OnEvent(events.auth.sendResetPassword)
|
||||
async handleSendResetPasswordMail(payload: IAuthSendedResetPassword) {
|
||||
await this.sendResetPasswordMailQueue.add(
|
||||
SendResetPasswordMailJob,
|
||||
{
|
||||
user: payload.user,
|
||||
token: payload.token,
|
||||
} as SendResetPasswordMailJobPayload,
|
||||
{
|
||||
delay: 0,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user