This commit is contained in:
2026-06-28 20:32:52 +03:30
parent 7a5b3f0bea
commit bf35179d55
45 changed files with 1555 additions and 46 deletions
+6 -1
View File
@@ -1,4 +1,5 @@
import { IsOptional, IsString, Length, Matches } from 'class-validator';
import { IsEnum, IsOptional, IsString, Length, Matches } from 'class-validator';
import { UserRole } from '../enums/user-role.enum';
export class UpdateUserDto {
@IsOptional()
@@ -14,4 +15,8 @@ export class UpdateUserDto {
@IsOptional()
@Matches(/^09\d{9}$/)
mobile?: string;
@IsOptional()
@IsEnum(UserRole)
role?: UserRole;
}
+29 -2
View File
@@ -2,32 +2,53 @@ import {
Body,
Controller,
Delete,
ForbiddenException,
Get,
Param,
ParseUUIDPipe,
Patch,
Post,
UseGuards,
} from '@nestjs/common';
import { PermissionCode } from '../authorization/enums/permission-code.enum';
import { CurrentUser } from '../auth/decorators/current-user.decorator';
import { Permissions } from '../auth/decorators/permissions.decorator';
import { Roles } from '../auth/decorators/roles.decorator';
import { PermissionsGuard } from '../auth/guards/permissions.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { AuthUser } from '../auth/interfaces/auth-user.interface';
import { UserRole } from './enums/user-role.enum';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { UsersService } from './users.service';
@Controller('users')
@UseGuards(RolesGuard, PermissionsGuard)
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Roles(UserRole.ADMIN)
@Permissions(PermissionCode.USERS_WRITE)
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Roles(UserRole.ADMIN)
@Permissions(PermissionCode.USERS_READ)
@Get()
findAll() {
return this.usersService.findAll();
}
@Get(':id')
findOne(@Param('id', ParseUUIDPipe) id: string) {
findOne(
@Param('id', ParseUUIDPipe) id: string,
@CurrentUser() user: AuthUser,
) {
if (user.role !== UserRole.ADMIN && user.id !== id) {
throw new ForbiddenException();
}
return this.usersService.findOne(id);
}
@@ -35,10 +56,16 @@ export class UsersController {
update(
@Param('id', ParseUUIDPipe) id: string,
@Body() updateUserDto: UpdateUserDto,
@CurrentUser() user: AuthUser,
) {
return this.usersService.update(id, updateUserDto);
if (user.role !== UserRole.ADMIN && user.id !== id) {
throw new ForbiddenException();
}
return this.usersService.update(id, updateUserDto, user);
}
@Roles(UserRole.ADMIN)
@Permissions(PermissionCode.USERS_DELETE)
@Delete(':id')
remove(@Param('id', ParseUUIDPipe) id: string) {
return this.usersService.remove(id);
+2 -1
View File
@@ -1,11 +1,12 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthGuardsModule } from '../auth/auth-guards.module';
import { User } from './entities/user.entity';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
@Module({
imports: [TypeOrmModule.forFeature([User])],
imports: [TypeOrmModule.forFeature([User]), AuthGuardsModule],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
+18 -1
View File
@@ -1,13 +1,16 @@
import {
ConflictException,
ForbiddenException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { AuthUser } from '../auth/interfaces/auth-user.interface';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User } from './entities/user.entity';
import { UserRole } from './enums/user-role.enum';
@Injectable()
export class UsersService {
@@ -34,11 +37,25 @@ export class UsersService {
return user;
}
async update(id: string, updateUserDto: UpdateUserDto): Promise<User> {
async findByMobile(mobile: string): Promise<User | null> {
return this.usersRepository.findOneBy({ mobile });
}
async update(
id: string,
updateUserDto: UpdateUserDto,
currentUser?: AuthUser,
): Promise<User> {
const user = await this.findOne(id);
if (updateUserDto.mobile) {
await this.ensureMobileIsUnique(updateUserDto.mobile, id);
}
if (
updateUserDto.role !== undefined &&
currentUser?.role !== UserRole.ADMIN
) {
throw new ForbiddenException('Only admin can change user role');
}
Object.assign(user, updateUserDto);
return this.usersRepository.save(user);
}