Add modulos de usuario
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
DATABASE_URL="postgresql://username:password@ip_server:port/database?schema=public"
|
||||
REDIS_URL="redis://username:password@ip_server:port"
|
||||
REDIS_URL="redis://username:password@ip_server:port"
|
||||
SESSION_TTL_SECONDS=3600
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"@nestjs/core": "11.0.1",
|
||||
"@nestjs/platform-express": "11.0.1",
|
||||
"@prisma/client": "6.14.0",
|
||||
"bcrypt": "6.0.0",
|
||||
"class-transformer": "0.5.1",
|
||||
"class-validator": "0.14.2",
|
||||
"dayjs": "1.11.13",
|
||||
@@ -36,6 +37,7 @@
|
||||
"@nestjs/cli": "11.0.0",
|
||||
"@nestjs/schematics": "11.0.0",
|
||||
"@nestjs/testing": "11.0.1",
|
||||
"@types/bcrypt": "6.0.0",
|
||||
"@types/express": "5.0.0",
|
||||
"@types/jest": "30.0.0",
|
||||
"@types/node": "22.10.7",
|
||||
|
||||
@@ -29,6 +29,11 @@ enum status_usuario {
|
||||
EXCLUIDO
|
||||
}
|
||||
|
||||
enum eboolean {
|
||||
V
|
||||
F
|
||||
}
|
||||
|
||||
model videos {
|
||||
id Int @id @default(autoincrement())
|
||||
uuid String @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
@@ -54,7 +59,7 @@ model Usuario {
|
||||
id Int @id @default(autoincrement())
|
||||
uuid String @unique @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
email String @unique @db.VarChar(255)
|
||||
email_verificado Boolean @default(false)
|
||||
email_verificado eboolean @default(F)
|
||||
password String @db.VarChar(255)
|
||||
nome String? @db.VarChar(100)
|
||||
sobrenome String? @db.VarChar(100)
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppController', () => {
|
||||
let appController: AppController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const app: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
|
||||
appController = app.get<AppController>(AppController);
|
||||
});
|
||||
|
||||
describe('root', () => {
|
||||
it('should return "Hello World!"', () => {
|
||||
expect(appController.getHello()).toBe('Hello World!');
|
||||
});
|
||||
});
|
||||
});
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppController', () => {
|
||||
let appController: AppController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const app: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
|
||||
appController = app.get<AppController>(AppController);
|
||||
});
|
||||
|
||||
describe('root', () => {
|
||||
it('should return "Hello World!"', () => {
|
||||
expect(appController.getHello()).toBe('Hello World!');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { PrismaModule } from './prisma/prisma.module';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { VideosModule } from './videos/videos.module';
|
||||
import { UsuariosModule } from './usuarios/usuarios.module';
|
||||
import { RedisModule } from './redis/redis.module';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, VideosModule],
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
PrismaModule,
|
||||
RedisModule,
|
||||
AuthModule,
|
||||
VideosModule,
|
||||
UsuariosModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
})
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
getHello(): string {
|
||||
return 'Hello World!';
|
||||
}
|
||||
}
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
getHello(): string {
|
||||
return 'Hello World!';
|
||||
}
|
||||
}
|
||||
|
||||
0
src/redis/redis.module.ts
Normal file
0
src/redis/redis.module.ts
Normal file
19
src/usuarios/dto/create-usuario-dto.ts
Normal file
19
src/usuarios/dto/create-usuario-dto.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { IsString, MaxLength } from 'class-validator';
|
||||
|
||||
export class CreateUsuarioDto {
|
||||
@IsString()
|
||||
@MaxLength(244)
|
||||
nome!: string;
|
||||
|
||||
@IsString()
|
||||
@MaxLength(244)
|
||||
sobrenome!: string;
|
||||
|
||||
@IsString()
|
||||
@MaxLength(244)
|
||||
email!: string;
|
||||
|
||||
@IsString()
|
||||
@MaxLength(244)
|
||||
password!: string;
|
||||
}
|
||||
53
src/usuarios/dto/usuarios.response.ts
Normal file
53
src/usuarios/dto/usuarios.response.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import dayjs from 'dayjs';
|
||||
import { Expose, Transform, TransformFnParams } from 'class-transformer';
|
||||
import { papel_usuario, status_usuario, eboolean } from 'generated/prisma';
|
||||
|
||||
export class UsuariosResponseDto {
|
||||
@Expose()
|
||||
id!: number;
|
||||
|
||||
@Expose({ name: 'nome' })
|
||||
nome!: string | null;
|
||||
|
||||
@Expose()
|
||||
sobrenome!: string | null;
|
||||
|
||||
@Expose()
|
||||
uuid!: string | null;
|
||||
|
||||
@Expose()
|
||||
email!: string | null;
|
||||
|
||||
@Expose({ name: 'email_verificado' })
|
||||
email_verificado!: eboolean;
|
||||
|
||||
@Expose()
|
||||
papel!: papel_usuario;
|
||||
|
||||
@Expose()
|
||||
status!: status_usuario;
|
||||
|
||||
@Expose({ name: 'criado_em' })
|
||||
@Transform(
|
||||
({ value }: TransformFnParams) =>
|
||||
value ? dayjs(value as Date | string).format('DD/MM/YYYY HH:mm:ss') : '',
|
||||
{ toPlainOnly: true },
|
||||
)
|
||||
criado_em!: string;
|
||||
|
||||
@Expose({ name: 'atualizado_em' })
|
||||
@Transform(
|
||||
({ value }: TransformFnParams) =>
|
||||
value ? dayjs(value as Date | string).format('DD/MM/YYYY HH:mm:ss') : '',
|
||||
{ toPlainOnly: true },
|
||||
)
|
||||
atualizado_em!: string;
|
||||
|
||||
@Expose({ name: 'bloqueado_ate' })
|
||||
@Transform(
|
||||
({ value }: TransformFnParams) =>
|
||||
value ? dayjs(value as Date | string).format('DD/MM/YYYY HH:mm:ss') : '',
|
||||
{ toPlainOnly: true },
|
||||
)
|
||||
bloqueado_ate!: string;
|
||||
}
|
||||
47
src/usuarios/usuarios.controller.ts
Normal file
47
src/usuarios/usuarios.controller.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Body,
|
||||
UsePipes,
|
||||
ValidationPipe,
|
||||
Patch,
|
||||
} from '@nestjs/common';
|
||||
import { UsuariosService } from './usuarios.service';
|
||||
import { UsuariosResponseDto } from './dto/usuarios.response';
|
||||
import { CreateUsuarioDto } from './dto/create-usuario-dto';
|
||||
|
||||
@Controller('usuarios')
|
||||
export class UsuariosController {
|
||||
constructor(private readonly usuariosService: UsuariosService) {}
|
||||
|
||||
@Get()
|
||||
async list(): Promise<UsuariosResponseDto[]> {
|
||||
return this.usuariosService.list();
|
||||
}
|
||||
|
||||
@Get(':uuid')
|
||||
async get(@Param('uuid') uuid: string): Promise<UsuariosResponseDto> {
|
||||
return this.usuariosService.get(uuid);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@UsePipes(
|
||||
new ValidationPipe({
|
||||
whitelist: true,
|
||||
forbidNonWhitelisted: true,
|
||||
transform: true,
|
||||
}),
|
||||
)
|
||||
async create(@Body() body: CreateUsuarioDto): Promise<UsuariosResponseDto> {
|
||||
return this.usuariosService.create(body);
|
||||
}
|
||||
|
||||
@Patch(':uuid/email-verificado')
|
||||
async emailVerificado(
|
||||
@Param('uuid') uuid: string,
|
||||
): Promise<UsuariosResponseDto> {
|
||||
return this.usuariosService.emailVerificado(uuid);
|
||||
}
|
||||
}
|
||||
11
src/usuarios/usuarios.module.ts
Normal file
11
src/usuarios/usuarios.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UsuariosService } from './usuarios.service';
|
||||
import { UsuariosController } from './usuarios.controller';
|
||||
import { PrismaModule } from '../prisma/prisma.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
providers: [UsuariosService],
|
||||
controllers: [UsuariosController],
|
||||
})
|
||||
export class UsuariosModule {}
|
||||
141
src/usuarios/usuarios.service.ts
Normal file
141
src/usuarios/usuarios.service.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
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';
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class UsuariosService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async list(): Promise<UsuariosResponseDto[]> {
|
||||
const data = await this.prisma.usuario.findMany({
|
||||
where: { email: { not: 'admin@clipperia.com' } },
|
||||
orderBy: { id: 'desc' },
|
||||
select: SELECT,
|
||||
});
|
||||
|
||||
return plainToInstance(UsuariosResponseDto, data, {
|
||||
excludeExtraneousValues: true,
|
||||
});
|
||||
}
|
||||
|
||||
async listPaginated(
|
||||
page: number,
|
||||
perPage: number,
|
||||
direction: 'asc' | 'desc' = 'desc',
|
||||
): Promise<PaginatedResponse<UsuariosResponseDto>> {
|
||||
const skip = page >= 1 ? page * perPage : 0;
|
||||
|
||||
const [rows, total] = await Promise.all([
|
||||
this.prisma.usuario.findMany({
|
||||
orderBy: { id: direction },
|
||||
skip,
|
||||
take: perPage ?? 1,
|
||||
select: SELECT,
|
||||
}),
|
||||
this.prisma.usuario.count(),
|
||||
]);
|
||||
|
||||
const content: UsuariosResponseDto[] = plainToInstance(
|
||||
UsuariosResponseDto,
|
||||
rows,
|
||||
{ excludeExtraneousValues: true },
|
||||
);
|
||||
|
||||
const totalPages = Math.max(1, Math.ceil(total / perPage));
|
||||
|
||||
return {
|
||||
content,
|
||||
pagination: {
|
||||
page,
|
||||
direction,
|
||||
perPage,
|
||||
total,
|
||||
totalPages,
|
||||
hasNext: page < totalPages,
|
||||
hasPrev: page > 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@ import { videos, Prisma, video_situation } from 'generated/prisma';
|
||||
|
||||
import { VideosService } from './videos.service';
|
||||
import { VideoResponseDto } from './dto/video-response.dto';
|
||||
import { PaginatedQueryDto, PaginatedResponse } from './dto/paginated.dto';
|
||||
import { EBooleanPipe } from './videos.pipe';
|
||||
import { PaginatedQueryDto, PaginatedResponse } from '../shared/dto/paginated';
|
||||
import { EBooleanPipe } from '../shared/pipe';
|
||||
|
||||
@Controller('videos')
|
||||
export class VideosController {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Prisma, videos, video_situation } from 'generated/prisma';
|
||||
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { VideoResponseDto } from './dto/video-response.dto';
|
||||
import { PaginatedResponse } from './dto/paginated.dto';
|
||||
import { PaginatedResponse } from '../shared/dto/paginated';
|
||||
|
||||
@Injectable()
|
||||
export class VideosService {
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import * as request from 'supertest';
|
||||
import { App } from 'supertest/types';
|
||||
import { AppModule } from './../src/app.module';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication<App>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('/ (GET)', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Hello World!');
|
||||
});
|
||||
});
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import request from 'supertest';
|
||||
import { App } from 'supertest/types';
|
||||
import { AppModule } from './../src/app.module';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication<App>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('/ (GET)', () =>
|
||||
request(app.getHttpServer()).get('/').expect(200).expect('Hello World!'));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user