Files
clipperia-api/src/modules/usuarios/usuarios.service.ts
2025-11-02 20:40:34 -03:00

196 lines
4.7 KiB
TypeScript

import bcrypt from 'bcrypt';
import { Injectable, NotFoundException } from '@nestjs/common';
import { plainToInstance } from 'class-transformer';
import { Prisma, Usuario } from 'generated/prisma';
import { PrismaService } from '@prisma/prisma.service';
import { PaginatedResponse } from '@shared/dto/paginated';
import { UsuariosResponseDto } from './dto/usuarios.response';
import { CreateUsuarioDto } from './dto/create-usuario-dto';
type ListUsuariosFilters = {
name?: string;
email?: string;
};
type ListUsuariosPaginatedParams = ListUsuariosFilters & {
page?: number;
perPage?: number;
direction?: 'asc' | 'desc';
};
const SELECT = {
id: true,
uuid: true,
email: true,
email_verificado: true,
nome: true,
sobrenome: true,
papel: true,
status: true,
ultimo_login_em: true,
ultimo_login_ip: true,
tentativas_login_falhadas: true,
bloqueado_ate: true,
criado_em: true,
atualizado_em: true,
} as const;
@Injectable()
export class UsuariosService {
constructor(private readonly prisma: PrismaService) {}
private buildWhere({
name,
email,
}: ListUsuariosFilters): Prisma.UsuarioWhereInput {
const andConditions: Prisma.UsuarioWhereInput[] = [
{ email: { not: 'admin@clipperia.com' } },
];
if (name) {
andConditions.push({
OR: [
{ nome: { contains: name, mode: 'insensitive' } },
{ sobrenome: { contains: name, mode: 'insensitive' } },
],
});
}
if (email) {
andConditions.push({ email: { contains: email, mode: 'insensitive' } });
}
return { AND: andConditions };
}
async list(
filters: ListUsuariosFilters = {},
): Promise<UsuariosResponseDto[]> {
const where = this.buildWhere(filters);
const data = await this.prisma.usuario.findMany({
where,
orderBy: { id: 'desc' },
select: SELECT,
});
return plainToInstance(UsuariosResponseDto, data, {
excludeExtraneousValues: true,
});
}
async listPaginated(
params: ListUsuariosPaginatedParams,
): Promise<PaginatedResponse<UsuariosResponseDto>> {
const parsedPage = Number(params.page);
const safePage =
Number.isFinite(parsedPage) && parsedPage > 0
? Math.floor(parsedPage)
: 1;
const parsedPerPage = Number(params.perPage);
const safePerPage =
Number.isFinite(parsedPerPage) && parsedPerPage > 0
? Math.floor(parsedPerPage)
: 10;
const safeDirection: 'asc' | 'desc' =
params.direction === 'asc' ? 'asc' : 'desc';
const where = this.buildWhere(params);
const skip = (safePage - 1) * safePerPage;
const [rows, total] = await Promise.all([
this.prisma.usuario.findMany({
where,
orderBy: { id: safeDirection },
skip,
take: safePerPage,
select: SELECT,
}),
this.prisma.usuario.count({ where }),
]);
const content: UsuariosResponseDto[] = plainToInstance(
UsuariosResponseDto,
rows,
{ excludeExtraneousValues: true },
);
const totalPages = Math.max(1, Math.ceil(total / safePerPage));
return {
content,
pagination: {
page: safePage,
direction: safeDirection,
perPage: safePerPage,
total,
totalPages,
hasNext: safePage < totalPages,
hasPrev: safePage > 1,
},
};
}
async get(uuid: string): Promise<UsuariosResponseDto> {
const row = await this.prisma.usuario.findUnique({
where: { uuid },
select: SELECT,
});
if (!row) {
throw new NotFoundException('Usuário não encontrado');
}
return plainToInstance(UsuariosResponseDto, row, {
excludeExtraneousValues: true,
});
}
async update(id: number, data: Prisma.UsuarioUpdateInput): Promise<Usuario> {
return this.prisma.usuario.update({
where: { id },
data,
});
}
async create(dto: CreateUsuarioDto): Promise<UsuariosResponseDto> {
const { email, password, nome, sobrenome } = dto;
const senhaCriptografada = await bcrypt.hash(password, 10);
const usuario = await this.prisma.usuario.create({
data: {
email,
password: senhaCriptografada,
nome,
sobrenome,
},
});
return plainToInstance(UsuariosResponseDto, usuario, {
excludeExtraneousValues: true,
});
}
async delete(id: number): Promise<Usuario> {
return this.prisma.usuario.delete({
where: { id },
});
}
async emailVerificado(uuid: string): Promise<UsuariosResponseDto> {
const usuario = await this.prisma.usuario.update({
where: { uuid },
data: { email_verificado: 'V' },
});
return plainToInstance(UsuariosResponseDto, usuario, {
excludeExtraneousValues: true,
});
}
}