mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 04:10:32 +00:00
feat(nestjs): resend the auth confirmation message
This commit is contained in:
@@ -72,3 +72,7 @@ export interface IAuthSignUpVerifiedEventPayload {
|
||||
verifyToken: string;
|
||||
userId: number;
|
||||
}
|
||||
|
||||
export interface ISignUpConfigmResendedEventPayload {
|
||||
user: SystemUser;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import { GetAuthMetaService } from './queries/GetAuthMeta.service';
|
||||
import { AuthedController } from './Authed.controller';
|
||||
import { GetAuthenticatedAccount } from './queries/GetAuthedAccount.service';
|
||||
import { TenancyModule } from '../Tenancy/Tenancy.module';
|
||||
import { EnsureUserVerifiedGuard } from './guards/EnsureUserVerified.guard';
|
||||
|
||||
const models = [InjectSystemModel(PasswordReset)];
|
||||
|
||||
@@ -73,6 +74,10 @@ const models = [InjectSystemModel(PasswordReset)];
|
||||
provide: APP_GUARD,
|
||||
useClass: JwtAuthGuard,
|
||||
},
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: EnsureUserVerifiedGuard,
|
||||
},
|
||||
AuthMailSubscriber,
|
||||
],
|
||||
})
|
||||
|
||||
@@ -8,6 +8,7 @@ import { AuthSignupDto } from './dtos/AuthSignup.dto';
|
||||
import { AuthSendResetPasswordService } from './commands/AuthSendResetPassword.service';
|
||||
import { AuthResetPasswordService } from './commands/AuthResetPassword.service';
|
||||
import { GetAuthMetaService } from './queries/GetAuthMeta.service';
|
||||
import { GetAuthenticatedAccount } from './queries/GetAuthedAccount.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthenticationApplication {
|
||||
@@ -19,6 +20,7 @@ export class AuthenticationApplication {
|
||||
private readonly authResetPasswordService: AuthResetPasswordService,
|
||||
private readonly authSendResetPasswordService: AuthSendResetPasswordService,
|
||||
private readonly authGetMeta: GetAuthMetaService,
|
||||
private readonly getAuthedAccountService: GetAuthenticatedAccount,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -53,8 +55,8 @@ export class AuthenticationApplication {
|
||||
* @param {number} userId - System user id.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async signUpConfirmResend(userId: number) {
|
||||
return this.authSignUpConfirmResendService.signUpConfirmResend(userId);
|
||||
public async signUpConfirmResend() {
|
||||
return this.authSignUpConfirmResendService.signUpConfirmResend();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,4 +85,11 @@ export class AuthenticationApplication {
|
||||
public async getAuthMeta() {
|
||||
return this.authGetMeta.getAuthMeta();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the authenticated account meta.
|
||||
*/
|
||||
public getAuthedAccount() {
|
||||
return this.getAuthedAccountService.getAccount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,47 @@
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { ApiBody, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { GetAuthenticatedAccount } from './queries/GetAuthedAccount.service';
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { Controller, Get, Post } from '@nestjs/common';
|
||||
import { IgnoreTenantSeededRoute } from '../Tenancy/EnsureTenantIsSeeded.guards';
|
||||
import { IgnoreTenantInitializedRoute } from '../Tenancy/EnsureTenantIsInitialized.guard';
|
||||
import { AuthenticationApplication } from './AuthApplication.sevice';
|
||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||
import { IgnoreUserVerifiedRoute } from './guards/EnsureUserVerified.guard';
|
||||
|
||||
@Controller('/auth')
|
||||
@ApiTags('Auth')
|
||||
@IgnoreTenantSeededRoute()
|
||||
@IgnoreTenantInitializedRoute()
|
||||
@IgnoreUserVerifiedRoute()
|
||||
export class AuthedController {
|
||||
constructor(
|
||||
private readonly getAuthedAccountService: GetAuthenticatedAccount,
|
||||
private readonly authApp: AuthenticationApplication,
|
||||
private readonly tenancyContext: TenancyContext,
|
||||
) {}
|
||||
|
||||
@Post('/signup/confirm/resend')
|
||||
@ApiOperation({ summary: 'Resend the signup confirmation message' })
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
code: { type: 'number', example: 200 },
|
||||
message: { type: 'string', example: 'resent successfully.' },
|
||||
},
|
||||
},
|
||||
})
|
||||
async resendSignupConfirm() {
|
||||
await this.authApp.signUpConfirmResend();
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: 'The signup confirmation message has been resent successfully.',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('/account')
|
||||
@ApiOperation({ summary: 'Retrieve the authenticated account' })
|
||||
async getAuthedAcccount() {
|
||||
const data = await this.getAuthedAccountService.getAccount();
|
||||
|
||||
return { data };
|
||||
return this.getAuthedAccountService.getAccount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,11 +41,6 @@ export class AuthSigninService {
|
||||
`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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,42 @@
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { SystemUser } from '@/modules/System/models/SystemUser';
|
||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||
import { ERRORS } from '../Auth.constants';
|
||||
import { events } from '@/common/events/events';
|
||||
import { ModelObject } from 'objection';
|
||||
import { ISignUpConfigmResendedEventPayload } from '../Auth.interfaces';
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthSignupConfirmResendService {
|
||||
signUpConfirmResend(userId: number) {
|
||||
return;
|
||||
constructor(
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
private readonly tenancyContext: TenancyContext,
|
||||
|
||||
@Inject(SystemUser.name)
|
||||
private readonly systemUserModel: typeof SystemUser,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Resends the email confirmation of the given user.
|
||||
* @param {number} userId - System User ID.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async signUpConfirmResend() {
|
||||
const user = await this.tenancyContext.getSystemUser();
|
||||
|
||||
// Throw error if the user is already verified.
|
||||
if (user.verified) {
|
||||
throw new ServiceError(ERRORS.USER_ALREADY_VERIFIED);
|
||||
}
|
||||
// Throw error if the verification token is not exist.
|
||||
if (!user.verifyToken) {
|
||||
throw new ServiceError(ERRORS.USER_ALREADY_VERIFIED);
|
||||
}
|
||||
// Triggers `signUpConfirmResended` event.
|
||||
await this.eventPublisher.emitAsync(events.auth.signUpConfirmResended, {
|
||||
user,
|
||||
} as ISignUpConfigmResendedEventPayload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
SetMetadata,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { IS_PUBLIC_ROUTE } from '../Auth.constants';
|
||||
|
||||
export const IS_IGNORE_USER_VERIFIED = 'IS_IGNORE_USER_VERIFIED';
|
||||
export const IgnoreUserVerifiedRoute = () =>
|
||||
SetMetadata(IS_IGNORE_USER_VERIFIED, true);
|
||||
|
||||
@Injectable()
|
||||
export class EnsureUserVerifiedGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly tenancyContext: TenancyContext,
|
||||
private readonly reflector: Reflector,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validate the authenticated user if verified and throws exception if not.
|
||||
* @param {ExecutionContext} context
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const isIgnoredUserVerified = this.reflector.getAllAndOverride<boolean>(
|
||||
IS_IGNORE_USER_VERIFIED,
|
||||
[context.getHandler(), context.getClass()],
|
||||
);
|
||||
const isPublic = this.reflector.getAllAndOverride<boolean>(
|
||||
IS_PUBLIC_ROUTE,
|
||||
[context.getHandler(), context.getClass()],
|
||||
);
|
||||
// Skip the guard early if the route marked as public or ignored.
|
||||
if (isPublic || isIgnoredUserVerified) {
|
||||
return true;
|
||||
}
|
||||
const systemUser = await this.tenancyContext.getSystemUser();
|
||||
|
||||
if (!systemUser.verified) {
|
||||
throw new UnauthorizedException(
|
||||
`The user is not verified yet, check out your mail inbox.`,
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { OnEvent } from '@nestjs/event-emitter';
|
||||
import {
|
||||
IAuthSendedResetPassword,
|
||||
IAuthSignedUpEventPayload,
|
||||
ISignUpConfigmResendedEventPayload,
|
||||
} from '../Auth.interfaces';
|
||||
import { Queue } from 'bullmq';
|
||||
import { InjectQueue } from '@nestjs/bullmq';
|
||||
@@ -27,12 +28,15 @@ export class AuthMailSubscriber {
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param {IAuthSignedUpEventPayload} payload
|
||||
* @param {IAuthSignedUpEventPayload | ISignUpConfigmResendedEventPayload} payload
|
||||
*/
|
||||
@OnEvent(events.auth.signUp)
|
||||
async handleSignupSendVerificationMail(payload: IAuthSignedUpEventPayload) {
|
||||
@OnEvent(events.auth.signUpConfirmResended)
|
||||
async handleSignupSendVerificationMail(
|
||||
payload: IAuthSignedUpEventPayload | ISignUpConfigmResendedEventPayload,
|
||||
) {
|
||||
try {
|
||||
const job = await this.sendSignupVerificationMailQueue.add(
|
||||
await this.sendSignupVerificationMailQueue.add(
|
||||
SendSignupVerificationMailJob,
|
||||
{
|
||||
email: payload.user.email,
|
||||
@@ -43,7 +47,6 @@ export class AuthMailSubscriber {
|
||||
delay: 0,
|
||||
},
|
||||
);
|
||||
console.log(job);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user