Refatoração do login page

This commit is contained in:
unknown
2025-03-28 17:48:56 -03:00
parent b98b219e52
commit 36aea127c1
15 changed files with 227 additions and 202 deletions

View File

@@ -17,10 +17,12 @@ import { HttpModule } from '@nestjs/axios';
import { LogisticController } from './logistic/logistic.controller';
import { LogisticService } from './logistic/logistic.service';
import { LoggerModule } from './Log/logger.module';
import { UsersModule } from './auth/users/users.module';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }), // necessário para ConfigService
UsersModule,
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRootAsync({
name: 'oracle',
inject: [ConfigService],

View File

@@ -9,10 +9,8 @@ import {
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersService } from '../users/users.service';
import { UserModel } from 'src/core/models/user.model';
import { ResultModel } from 'src/core/models/result.model';
import { ResetPasswordModel } from 'src/core/models/reset-password.model';
import { ChangePasswordModel } from 'src/core/models/change-password.model';
import { LoginDto } from '../dto/login.dto';
@Controller('api/v1/auth')
export class AuthController {
@@ -22,8 +20,12 @@ export class AuthController {
) { }
@Post('login')
async login(@Body() model: UserModel): Promise<any> {
const user = await this.usersService.authenticate(model);
async login(@Body() model: LoginDto): Promise<any> {
const user = await this.usersService.authenticate({
userName: model.username,
password: model.password,
});
if (!user)
throw new HttpException(
new ResultModel(false, 'Usuário ou senha inválidos.', null, null),
@@ -33,7 +35,7 @@ export class AuthController {
const token = await this.authService.createToken(
user.id,
user.sellerId,
user.username,
user.name,
user.email,
user.storeId
);
@@ -48,25 +50,4 @@ export class AuthController {
token: token,
};
}
@Post('reset-password')
async resetPassword(@Body() resetPassword: ResetPasswordModel) {
const response = await this.usersService.resetPassword(resetPassword);
if (response == null) {
throw new HttpException('Usuário não foi encontrado', HttpStatus.NOT_FOUND);
}
return { message: 'Senha alterada com sucesso! Foi enviado email com a nova senha!' };
}
@Post('change-password')
async changePassword(@Body() changePassword: ChangePasswordModel) {
const response = await this.usersService.changePassword(changePassword);
if (response == null) {
throw new HttpException('Usuário não foi encontrado', HttpStatus.NOT_FOUND);
}
return { message: 'Senha alterada com sucesso!' };
}
}

View File

@@ -0,0 +1,23 @@
import { Injectable, ForbiddenException } from '@nestjs/common';
import { UserRepository } from '../users/UserRepository';
@Injectable()
export class AuthenticateUserService {
constructor(private readonly userRepository: UserRepository) {}
async execute(username: string, password: string) {
const user = await this.userRepository.findByUsernameAndPassword(username, password);
if (!user) return null;
if (user.dataDesligamento !== null) {
throw new ForbiddenException('Usuário desligado da empresa, login não permitido!');
}
if (user.situacao === 'I') {
throw new ForbiddenException('Usuário inativo, login não permitido!');
}
return user;
}
}

View File

@@ -0,0 +1,5 @@
export class LoginDto {
username: string;
password: string;
}

16
src/auth/models/result.ts Normal file
View File

@@ -0,0 +1,16 @@
export class Result<T> {
private constructor(
public readonly success: boolean,
public readonly data?: T,
public readonly error?: string,
) {}
static ok<U>(data: U): Result<U> {
return new Result<U>(true, data);
}
static fail<U>(message: string): Result<U> {
return new Result<U>(false, undefined, message);
}
}

View File

@@ -0,0 +1,64 @@
import { Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
@Injectable()
export class UserRepository {
constructor(
@InjectDataSource('oracle') // especifica que queremos o DataSource da conexão 'oracle'
private readonly dataSource: DataSource,
) {}
async findByUsernameAndPassword(username: string, password: string) {
const sql = `
SELECT
PCEMPR.MATRICULA AS "id",
PCEMPR.NOME AS "name",
PCEMPR.CODUSUR AS "sellerId",
PCEMPR.CODFILIAL AS "storeId",
PCEMPR.EMAIL AS "email",
PCEMPR.DTDEMISSAO as "dataDesligamento",
PCEMPR.SITUACAO as "situacao"
FROM PCEMPR
WHERE PCEMPR.USUARIOBD = :1
AND PCEMPR.SENHABD = CRYPT(:2, PCEMPR.USUARIOBD)
`;
const users = await this.dataSource.query(sql, [
username.toUpperCase(),
password.toUpperCase(),
]);
return users[0] || null;
}
async findByCpfAndEmail(cpf: string, email: string) {
const sql = `
SELECT PCUSUARI.CODUSUR as "sellerId",
PCUSUARI.NOME as "name",
PCUSUARI.EMAIL as "email"
FROM PCUSUARI
WHERE REGEXP_REPLACE(PCUSUARI.CPF, '[^0-9]', '') = REGEXP_REPLACE(:1, '[^0-9]', '')
AND PCUSUARI.EMAIL = :2
`;
const users = await this.dataSource.query(sql, [cpf, email]);
return users[0] || null;
}
async updatePassword(sellerId: number, newPasswordHash: string) {
const sql = `
UPDATE PCUSUARI SET SENHALOGIN = :1 WHERE CODUSUR = :2
`;
await this.dataSource.query(sql, [newPasswordHash, sellerId]);
}
async findByIdAndPassword(sellerId: number, passwordHash: string) {
const sql = `
SELECT CODUSUR as "sellerId", NOME as "name", EMAIL as "email"
FROM PCUSUARI
WHERE CODUSUR = :1 AND SENHALOGIN = :2
`;
const result = await this.dataSource.query(sql, [sellerId, passwordHash]);
return result[0] || null;
}
}

View File

@@ -0,0 +1,23 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { UserRepository } from '../users/UserRepository';
import md5 = require('md5');
@Injectable()
export class ChangePasswordService {
constructor(private readonly userRepository: UserRepository) {}
async execute(userId: number, oldPassword: string, newPassword: string) {
const current = await this.userRepository.findByIdAndPassword(
userId,
md5(oldPassword).toUpperCase(),
);
if (!current) {
throw new NotFoundException('Usuário não encontrado ou senha inválida');
}
const newPasswordHash = md5(newPassword).toUpperCase();
await this.userRepository.updatePassword(userId, newPasswordHash);
return current;
}
}

View File

@@ -1,4 +0,0 @@
export class AuthRequestDto {
userName: string;
password: string;
}

View File

@@ -0,0 +1,18 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export class EmailService {
async sendPasswordReset(email: string, newPassword: string) {
const sql = `
INSERT INTO CORRESPONDENCIAS (
CORRESPONDENCIA_ID, DTINCLUSAO, TITULO, MENSAGEM, EMAIL, DESTINATARIO
) VALUES (
SEQ_CORRESPONDENCIAS.NEXTVAL, SYSDATE, 'Alteração de senha - CoteLivia',
'Sua nova senha para acesso ao portal COTELIVIA é ${newPassword}',
:email, :email
)
`;
console.log(`[Email enviado para ${email}] Senha: ${newPassword}`);
}
}

View File

@@ -1,6 +0,0 @@
export class ChangePasswordModel {
id: string;
currentPassword: string;
newPassword: string;
}

View File

@@ -1,5 +0,0 @@
export class ResetPasswordModel {
document: string;
email: string;
}

View File

@@ -1,10 +0,0 @@
export interface UserModel {
id: string;
name: string;
sellerId: string;
storeId: string;
email: string;
username: string;
dataDesligamento: Date | null;
situacao: string;
}

View File

@@ -0,0 +1,28 @@
import { Injectable } from '@nestjs/common';
import { UserRepository } from './UserRepository';
import { EmailService } from './email.service';
import { DataSource } from 'typeorm';
import { Guid } from 'guid-typescript';
import * as md5 from 'md5';
import { InjectDataSource } from '@nestjs/typeorm';
@Injectable()
export class ResetPasswordService {
constructor(
@InjectDataSource('oracle') private readonly dataSource: DataSource,
private readonly userRepository: UserRepository,
private readonly emailService: EmailService,
) {}
async execute(document: string, email: string) {
const user = await this.userRepository.findByCpfAndEmail(document, email);
if (!user) return null;
const newPassword = Guid.create().toString().substring(0, 8);
await this.userRepository.updatePassword(user.sellerId, md5(newPassword).toUpperCase());
await this.emailService.sendPasswordReset(user.email, newPassword);
return { ...user, newPassword };
}
}

View File

@@ -1,10 +1,25 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; // <-- importe aqui
import { UsersService } from './users.service';
import { UserRepository } from './UserRepository';
import { AuthenticateUserService } from '../auth/authenticate-user.service';
import { ResetPasswordService } from './reset-password.service';
import { ChangePasswordService } from './change-password.service';
import { EmailService } from './email.service';
@Module({
imports: [],
providers: [UsersService],
imports: [
TypeOrmModule.forFeature([]),
],
providers: [
UsersService,
UserRepository,
AuthenticateUserService,
ResetPasswordService,
ChangePasswordService,
EmailService,
],
exports: [UsersService],
})
export class UsersModule {}

View File

@@ -1,151 +1,26 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
import md5 = require('md5');
import { Guid } from "guid-typescript";
import { createOracleConfig } from '../../core/configs/typeorm.oracle.config';
import { ConfigService } from '@nestjs/config';
import { Injectable } from '@nestjs/common';
import { AuthenticateUserService } from '../auth/authenticate-user.service';
import { ResetPasswordService } from './reset-password.service';
import { ChangePasswordService } from './change-password.service';
@Injectable()
export class UsersService {
constructor(private readonly configService: ConfigService) {}
constructor(
private readonly authenticateUserService: AuthenticateUserService,
private readonly resetPasswordService: ResetPasswordService,
private readonly changePasswordService: ChangePasswordService,
) {}
async authenticate(user: any): Promise<any> {
const dataSource = new DataSource(createOracleConfig(this.configService));
await dataSource.initialize();
const queryRunner = dataSource.createQueryRunner();
await queryRunner.connect();
try {
const sql = `SELECT PCEMPR.MATRICULA AS "id"
,PCEMPR.NOME AS "name"
,PCEMPR.CODUSUR AS "sellerId"
,PCEMPR.CODFILIAL AS "storeId"
,PCEMPR.EMAIL AS "email"
,PCEMPR.DTDEMISSAO as "dataDesligamento"
,PCEMPR.SITUACAO as "situacao"
FROM PCEMPR
WHERE PCEMPR.USUARIOBD = '${user.userName}'
AND PCEMPR.SENHABD = CRYPT('${user.password.toUpperCase()}', PCEMPR.USUARIOBD) `;
const users = await queryRunner.manager.query(sql);
if (users.length == 0) {
return null;
async authenticate(user: { userName: string; password: string }) {
return this.authenticateUserService.execute(user.userName, user.password);
}
const userDb = users[0];
if ( userDb.dataDesligamento !== null ) {
throw new HttpException('Usuário desligado da empresa, login não permitido!', HttpStatus.FORBIDDEN);
async resetPassword(user: { document: string; email: string }) {
return this.resetPasswordService.execute(user.document, user.email);
}
if ( userDb.situacao == 'I' ) {
throw new HttpException('Usuário inativo, login não permitido!', HttpStatus.FORBIDDEN);
async changePassword(user: { id: number; password: string; newPassword: string }) {
return this.changePasswordService.execute(user.id, user.password, user.newPassword);
}
return userDb;
} finally {
await queryRunner.release();
await dataSource.destroy();
}
}
async resetPassword(user: any): Promise<any> {
const dataSource = new DataSource(createOracleConfig(this.configService));
await dataSource.initialize();
const queryRunner = dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
let sql =
'SELECT PCUSUARI.CODUSUR as "sellerId" ' +
' ,PCUSUARI.NOME as "name" ' +
' ,PCUSUARI.EMAIL as "email" ' +
' FROM PCUSUARI ' +
` WHERE REGEXP_REPLACE(PCUSUARI.CPF, '[^0-9]', '') = REGEXP_REPLACE(:1, '[^0-9]', '') ` +
` AND PCUSUARI.EMAIL = :2 `;
const users = await queryRunner.manager.query(sql, [
user.document,
user.email,
]);
if (users.length == 0) {
return null;
}
const guid = Guid.create();
console.log(guid.toString());
const password = guid.toString().substring(0, 8);
const newPassword = md5(password).toUpperCase();
console.log("Senha:" + newPassword)
sql = `UPDATE PCUSUARI SET ` +
` SENHALOGIN = :1 ` +
`WHERE CODUSUR = :2`;
await queryRunner.manager.query(sql, [newPassword, users[0].sellerId]);
const sqlEmail = `INSERT INTO CORRESPONDENCIAS ( CORRESPONDENCIA_ID, DTINCLUSAO, TITULO, MENSAGEM, EMAIL, DESTINATARIO )
VALUES ( SEQ_CORRESPONDENCIAS.NEXTVAL, SYSDATE, 'Alteração de email - CoteLivia',
'Sua senha para acesso ao portal COTELIVIA é ${password}', '${users[0].email}', '${users[0].email}' )`;
await queryRunner.manager.query(sqlEmail);
await queryRunner.commitTransaction();
const userDb = users[0];
return userDb;
} catch (error) {
await queryRunner.rollbackTransaction();
console.log(error);
throw new Error(error);
}
finally {
await queryRunner.release();
await dataSource.destroy();
}
}
async changePassword(user: any): Promise<any> {
const dataSource = new DataSource(createOracleConfig(this.configService));
await dataSource.initialize();
const queryRunner = dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
console.log(JSON.stringify(user));
try {
let sql =
'SELECT PCUSUARI.CODUSUR as "sellerId" ' +
' ,PCUSUARI.NOME as "name" ' +
' ,PCUSUARI.EMAIL as "email" ' +
' FROM PCUSUARI ' +
` WHERE PCUSUARI.CODUSUR = :1` +
` AND PCUSUARI.SENHALOGIN = :2 `;
const users = await queryRunner.manager.query(sql, [
user.id,
md5(user.password).toUpperCase(),
]);
if (users.length == 0) {
return null;
}
sql = `UPDATE PCUSUARI SET ` +
` SENHALOGIN = :1 ` +
`WHERE CODUSUR = :2`;
await queryRunner.manager.query(sql, [md5(user.newPassword).toUpperCase(), users[0].sellerId]);
await queryRunner.commitTransaction();
const userDb = users[0];
return userDb;
} catch (error) {
await queryRunner.rollbackTransaction();
console.log(error);
throw new Error(error);
}
finally {
await queryRunner.release();
await dataSource.destroy();
}
}
}