mashafrancis/butternut-user

View on GitHub
src/app/auth/auth.controller.ts

Summary

Maintainability
C
1 day
Test Coverage
import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
import { Client, ClientProxy, Transport } from '@nestjs/microservices';
import { ApiBody, ApiResponse, ApiTags } from '@nestjs/swagger';
import { config } from '../../config';
import { RestException } from '../_helpers';
import { DeepPartial } from '../_helpers/database';
import { AppLogger } from '../app.logger';
import {
    USER_CMD_PASSWORD_NEW,
    USER_CMD_PASSWORD_RESET,
    USER_CMD_REGISTER,
    USER_CMD_REGISTER_VERIFY,
} from '../user';
import { UserEntity } from '../user/entity';
import { UserErrorEnum } from '../user/user-error.enum';
import { UserService } from '../user/user.service';
// import { AuthService } from './auth.userService';
import { CredentialsDto } from './dto/credentials.dto';
import { JwtDto } from './dto/jwt.dto';
import { PasswordResetDto } from './dto/password-reset.dto';
import { PasswordTokenDto } from './dto/password-token.dto';
import { UserEntityDto } from './dto/user-entity.dto';
import { VerifyResendDto } from './dto/verify-resend.dto';
import { VerifyTokenDto } from './dto/verify-token.dto';
import { createAuthToken, verifyToken } from './jwt';

@ApiTags('auth')
@Controller('auth')
export class AuthController {
    @Client({ transport: Transport.TCP })
    private client: ClientProxy;
    private logger = new AppLogger(AuthController.name);

    constructor(
        // private readonly authService: AuthService,
        private readonly userService: UserService,
    ) {}

    @Post('register')
    @HttpCode(204)
    @ApiBody({
        required: true,
        type: UserEntityDto,
        // name: 'UserEntityDto',
    })
    @ApiResponse({ status: 204, description: 'NO_CONTENT' })
    public async register(
        @Body() data: DeepPartial<UserEntity>,
    ): Promise<UserEntity> {
        const user = await this.userService.create(data);
        this.logger.debug(`[register] User ${data.email} registered`);
        // tslint:disable-next-line:no-empty
        this.client.send({ cmd: USER_CMD_REGISTER }, user).subscribe(
            () =>
                this.logger.debug(
                    `[register] Send registration email for email ${data.email}`,
                ),
            (error) => this.logger.error(error, ``),
        );
        return user;
    }

    @Post('login')
    @HttpCode(200)
    @ApiResponse({ status: 200, description: 'OK', type: JwtDto })
    public async login(@Body() credentials: CredentialsDto): Promise<JwtDto> {
        const user = this.userService.login(credentials);
        this.logger.debug(`[login] User ${credentials.email} login`);
        return createAuthToken(await user);
    }

    @Post('register/verify')
    @HttpCode(200)
    @ApiBody({
        required: true,
        type: VerifyTokenDto,
        // name: 'VerifyTokenDto',
    })
    @ApiResponse({ status: 200, description: 'OK', type: JwtDto })
    public async registerVerify(@Body() body: VerifyTokenDto): Promise<JwtDto> {
        this.logger.debug(`[registerVerify] Token ${body.verifyToken}`);
        const user = await this.userService.findByEmail(body.email);
        if (user.activationCode !== body.verifyToken) {
            throw new RestException(
                {
                    error: 'Auth',
                    message: `Wrong verification token`,
                    condition: UserErrorEnum.NOT_VERIFIED,
                },
                HttpStatus.UNPROCESSABLE_ENTITY,
            );
        }
        user.is_verified = true;
        await this.userService.update(user);
        this.client.send({ cmd: USER_CMD_REGISTER_VERIFY }, user).subscribe(
            () =>
                this.logger.debug(
                    `[registerVerify] Sent command register verify for user id ${user.id}`,
                ),
            (error) => this.logger.error(error, ''),
        );
        return createAuthToken(user);
    }

    @Post('register/verify/resend')
    @HttpCode(204)
    @ApiBody({
        required: true,
        type: VerifyResendDto,
        // name: 'VerifyResendDto',
    })
    @ApiResponse({ status: 204, description: 'NO CONTENT' })
    public async registerVerifyResend(
        @Body() body: VerifyResendDto,
    ): Promise<void> {
        try {
            this.logger.debug(
                `[registerVerifyResend] Email where resend verification ${body.email}`,
            );
            const user = await this.userService.findByEmail(body.email);
            if (user.is_verified) {
                throw new Error(`User ${user.email} already verified`);
            }
            this.client.send({ cmd: USER_CMD_REGISTER }, user).subscribe(
                () =>
                    this.logger.debug(
                        `[registerVerify] Sent command registry verify for email ${body.email}`,
                    ),
                (error) => this.logger.error(error, ''),
            );
        } catch (err) {
            this.logger.error(`[registerVerifyResend] ${err.message}`, err.stack);
        }
    }

    @Post('password/reset')
    @HttpCode(204)
    @ApiBody({
        required: true,
        type: PasswordResetDto,
        // name: 'PasswordResetDto',
    })
    @ApiResponse({ status: 204, description: 'NO CONTENT' })
    public passwordReset(@Body() data: DeepPartial<UserEntity>): void {
        this.logger.debug(
            `[passwordReset] User ${data.email} starts password reset`,
        );
        this.client
            .send({ cmd: USER_CMD_PASSWORD_RESET }, { email: data.email })
            .subscribe(
                () =>
                    this.logger.debug(
                        `[passwordReset] Sent command password reset for email ${data.email}`,
                    ),
                (error) => this.logger.error(error, ''),
            );
    }

    @Post('password/new')
    @HttpCode(204)
    @ApiBody({
        required: true,
        type: PasswordTokenDto,
        // name: 'PasswordTokenDto',
    })
    @ApiResponse({ status: 204, description: 'NO CONTENT' })
    public async passwordNew(@Body() body: PasswordTokenDto): Promise<void> {
        this.logger.debug(`[passwordNew] Token ${body.resetToken}`);
        const token = await verifyToken(
            body.resetToken,
            config.session.password_reset.secret,
        );
        const user = await this.userService.updatePassword({
            id: token.id,
            password: body.password,
        });
        this.logger.debug(
            `[passwordNew] Send change password email for user ${user.email}`,
        );
        this.client.send({ cmd: USER_CMD_PASSWORD_NEW }, user).subscribe(
            () =>
                this.logger.debug(
                    `[passwordNew] Sent command for new password for email ${user.email}`,
                ),
            (error) => this.logger.error(error, ''),
        );
    }
}