feat: StorageService S3/MinIO, POST /users, invoice upload, portal dashboard §08 statuses + notifications
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { Controller, Get, Patch, Body, Param, Query, UseGuards } from "@nestjs/common";
|
||||
import { Controller, Get, Post, Patch, Body, Param, Query, UseGuards } from "@nestjs/common";
|
||||
import { UsersService } from "./users.service";
|
||||
import { JwtAuthGuard, RolesGuard, Roles } from "../auth/guards/auth.guard";
|
||||
import { CurrentUser } from "../auth/decorators/current-user.decorator";
|
||||
import { IsEnum, IsBoolean } from "class-validator";
|
||||
import { IsEnum, IsBoolean, IsEmail, IsString, MinLength, IsOptional } from "class-validator";
|
||||
|
||||
class UpdateRoleDto {
|
||||
@IsEnum(["CLIENTE","OPERADOR_BODEGA","AGENTE_ADUANERO","ADMIN_EMPRESA","SUPER_ADMIN","SOPORTE"])
|
||||
@@ -11,6 +11,15 @@ class UpdateRoleDto {
|
||||
class SetActiveDto {
|
||||
@IsBoolean() isActive!: boolean;
|
||||
}
|
||||
class CreateUserDto {
|
||||
@IsEmail() email!: string;
|
||||
@IsString() @MinLength(8) password!: string;
|
||||
@IsString() firstName!: string;
|
||||
@IsString() lastName!: string;
|
||||
@IsOptional() @IsString() phone?: string;
|
||||
@IsEnum(["CLIENTE","OPERADOR_BODEGA","AGENTE_ADUANERO","ADMIN_EMPRESA","SUPER_ADMIN","SOPORTE"])
|
||||
role!: string;
|
||||
}
|
||||
|
||||
@Controller("users")
|
||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||
@@ -29,6 +38,13 @@ export class UsersController {
|
||||
return this.svc.findOne(id);
|
||||
}
|
||||
|
||||
/** Admin creates a user directly (doc §12) */
|
||||
@Post()
|
||||
@Roles("ADMIN_EMPRESA", "SUPER_ADMIN")
|
||||
create(@Body() dto: CreateUserDto, @CurrentUser() user: any): Promise<any> {
|
||||
return this.svc.create(dto, user.tenantId);
|
||||
}
|
||||
|
||||
@Patch(":id/role")
|
||||
@Roles("ADMIN_EMPRESA", "SUPER_ADMIN")
|
||||
updateRole(@Param("id") id: string, @Body() dto: UpdateRoleDto): Promise<any> {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { UsersController } from "./users.controller";
|
||||
import { UsersService } from "./users.service";
|
||||
import { PrismaModule } from "../prisma/prisma.module";
|
||||
|
||||
@Module({ controllers: [UsersController], providers: [UsersService] })
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
exports: [UsersService],
|
||||
})
|
||||
export class UsersModule {}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { Injectable, NotFoundException, ConflictException } from "@nestjs/common";
|
||||
import { PrismaService } from "../prisma/prisma.service";
|
||||
import * as bcrypt from "bcrypt";
|
||||
import { generateSuiteCode } from "../common/utils/suite-code.util";
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
@@ -39,6 +41,52 @@ export class UsersService {
|
||||
return user;
|
||||
}
|
||||
|
||||
/** Admin creates a user directly (no self-registration) */
|
||||
async create(dto: {
|
||||
email: string;
|
||||
password: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
phone?: string;
|
||||
role: string;
|
||||
}, tenantId: string): Promise<any> {
|
||||
const exists = await this.prisma.client.user.findFirst({
|
||||
where: { email: dto.email, tenantId },
|
||||
});
|
||||
if (exists) throw new ConflictException("Ya existe un usuario con ese email.");
|
||||
|
||||
const hash = await bcrypt.hash(dto.password, 12);
|
||||
|
||||
// Generate suite code for CLIENTE role
|
||||
let suiteCode: string | undefined;
|
||||
if (dto.role === "CLIENTE") {
|
||||
const count = await this.prisma.client.user.count({ where: { tenantId } });
|
||||
suiteCode = generateSuiteCode(count + 1);
|
||||
}
|
||||
|
||||
const user = await this.prisma.client.user.create({
|
||||
data: {
|
||||
tenantId,
|
||||
email: dto.email,
|
||||
passwordHash: hash,
|
||||
firstName: dto.firstName,
|
||||
lastName: dto.lastName,
|
||||
phone: dto.phone,
|
||||
role: dto.role as any,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (suiteCode) {
|
||||
await this.prisma.client.suite.create({
|
||||
data: { tenantId, userId: user.id, code: suiteCode },
|
||||
});
|
||||
}
|
||||
|
||||
const { passwordHash: _, ...safe } = user as any;
|
||||
return safe;
|
||||
}
|
||||
|
||||
async updateRole(id: string, role: string): Promise<any> {
|
||||
return this.prisma.client.user.update({ where: { id }, data: { role: role as any } });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user