first commit
This commit is contained in:
64
myteamwallet_backend/src/users/dto/create-user.dto.ts
Normal file
64
myteamwallet_backend/src/users/dto/create-user.dto.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { Transform } from 'class-transformer';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Role } from '../../roles/entities/role.entity';
|
||||
import {
|
||||
IsEmail,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
MinLength,
|
||||
Validate,
|
||||
} from 'class-validator';
|
||||
import { Status } from '../../statuses/entities/status.entity';
|
||||
import { IsNotExist } from '../../utils/validators/is-not-exists.validator';
|
||||
import { FileEntity } from '../../files/entities/file.entity';
|
||||
import { IsExist } from '../../utils/validators/is-exists.validator';
|
||||
|
||||
export class CreateUserDto {
|
||||
@ApiProperty({ example: 'test1@example.com' })
|
||||
@Transform(({ value }) => value?.toLowerCase().trim())
|
||||
@IsNotEmpty()
|
||||
@Validate(IsNotExist, ['User'], {
|
||||
message: 'emailAlreadyExists',
|
||||
})
|
||||
@IsEmail()
|
||||
email: string | null;
|
||||
|
||||
@ApiProperty()
|
||||
@MinLength(6)
|
||||
password?: string;
|
||||
|
||||
provider?: string;
|
||||
|
||||
socialId?: string | null;
|
||||
|
||||
@ApiProperty({ example: 'John' })
|
||||
@IsNotEmpty()
|
||||
firstName: string | null;
|
||||
|
||||
@ApiProperty({ example: 'Doe' })
|
||||
@IsNotEmpty()
|
||||
lastName: string | null;
|
||||
|
||||
@ApiProperty({ type: () => FileEntity })
|
||||
@IsOptional()
|
||||
@Validate(IsExist, ['FileEntity', 'id'], {
|
||||
message: 'imageNotExists',
|
||||
})
|
||||
photo?: FileEntity | null;
|
||||
|
||||
@ApiProperty({ type: Role })
|
||||
@Validate(IsExist, ['Role', 'id'], {
|
||||
message: 'roleNotExists',
|
||||
})
|
||||
role?: Role | null;
|
||||
|
||||
@ApiProperty({ type: Status })
|
||||
@Validate(IsExist, ['Status', 'id'], {
|
||||
message: 'statusNotExists',
|
||||
})
|
||||
status?: Status;
|
||||
|
||||
hash?: string | null;
|
||||
|
||||
linkPlayerId?: number | null;
|
||||
}
|
||||
62
myteamwallet_backend/src/users/dto/update-user.dto.ts
Normal file
62
myteamwallet_backend/src/users/dto/update-user.dto.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateUserDto } from './create-user.dto';
|
||||
|
||||
import { Transform } from 'class-transformer';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Role } from '../../roles/entities/role.entity';
|
||||
import { IsEmail, IsOptional, MinLength, Validate } from 'class-validator';
|
||||
import { Status } from '../../statuses/entities/status.entity';
|
||||
import { IsNotExist } from '../../utils/validators/is-not-exists.validator';
|
||||
import { FileEntity } from '../../files/entities/file.entity';
|
||||
import { IsExist } from '../../utils/validators/is-exists.validator';
|
||||
|
||||
export class UpdateUserDto extends PartialType(CreateUserDto) {
|
||||
@ApiProperty({ example: 'test1@example.com' })
|
||||
@Transform(({ value }) => value?.toLowerCase().trim())
|
||||
@IsOptional()
|
||||
@Validate(IsNotExist, ['User'], {
|
||||
message: 'emailAlreadyExists',
|
||||
})
|
||||
@IsEmail()
|
||||
email?: string | null;
|
||||
|
||||
@ApiProperty()
|
||||
@IsOptional()
|
||||
@MinLength(6)
|
||||
password?: string;
|
||||
|
||||
provider?: string;
|
||||
|
||||
socialId?: string | null;
|
||||
|
||||
@ApiProperty({ example: 'John' })
|
||||
@IsOptional()
|
||||
firstName?: string | null;
|
||||
|
||||
@ApiProperty({ example: 'Doe' })
|
||||
@IsOptional()
|
||||
lastName?: string | null;
|
||||
|
||||
@ApiProperty({ type: () => FileEntity })
|
||||
@IsOptional()
|
||||
@Validate(IsExist, ['FileEntity', 'id'], {
|
||||
message: 'imageNotExists',
|
||||
})
|
||||
photo?: FileEntity | null;
|
||||
|
||||
@ApiProperty({ type: Role })
|
||||
@IsOptional()
|
||||
@Validate(IsExist, ['Role', 'id'], {
|
||||
message: 'roleNotExists',
|
||||
})
|
||||
role?: Role | null;
|
||||
|
||||
@ApiProperty({ type: Status })
|
||||
@IsOptional()
|
||||
@Validate(IsExist, ['Status', 'id'], {
|
||||
message: 'statusNotExists',
|
||||
})
|
||||
status?: Status;
|
||||
|
||||
hash?: string | null;
|
||||
}
|
||||
100
myteamwallet_backend/src/users/entities/user.entity.ts
Normal file
100
myteamwallet_backend/src/users/entities/user.entity.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import {
|
||||
Column,
|
||||
AfterLoad,
|
||||
CreateDateColumn,
|
||||
DeleteDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
BeforeInsert,
|
||||
BeforeUpdate,
|
||||
OneToMany,
|
||||
} from 'typeorm';
|
||||
import { Role } from '../../roles/entities/role.entity';
|
||||
import { Status } from '../../statuses/entities/status.entity';
|
||||
import { FileEntity } from '../../files/entities/file.entity';
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
import { EntityHelper } from 'src/utils/entity-helper';
|
||||
import { AuthProvidersEnum } from 'src/auth/auth-providers.enum';
|
||||
import { Exclude, Expose } from 'class-transformer';
|
||||
import { Player } from 'src/players/entities/player.entity';
|
||||
|
||||
@Entity()
|
||||
export class User extends EntityHelper {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ unique: true, nullable: true })
|
||||
email: string | null;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@Exclude({ toPlainOnly: true })
|
||||
password: string;
|
||||
|
||||
@Exclude({ toPlainOnly: true })
|
||||
public previousPassword: string;
|
||||
|
||||
@AfterLoad()
|
||||
public loadPreviousPassword(): void {
|
||||
this.previousPassword = this.password;
|
||||
}
|
||||
|
||||
@BeforeInsert()
|
||||
@BeforeUpdate()
|
||||
async setPassword() {
|
||||
if (this.previousPassword !== this.password && this.password) {
|
||||
const salt = await bcrypt.genSalt();
|
||||
this.password = await bcrypt.hash(this.password, salt);
|
||||
}
|
||||
}
|
||||
|
||||
@Column({ default: AuthProvidersEnum.email })
|
||||
@Expose({ groups: ['exposeProvider'] })
|
||||
provider: string;
|
||||
|
||||
@Index()
|
||||
@Column({ nullable: true })
|
||||
socialId: string | null;
|
||||
|
||||
@Index()
|
||||
@Column({ nullable: true })
|
||||
firstName: string | null;
|
||||
|
||||
@Index()
|
||||
@Column({ nullable: true })
|
||||
lastName: string | null;
|
||||
|
||||
@ManyToOne(() => FileEntity, {
|
||||
eager: true,
|
||||
})
|
||||
photo?: FileEntity | null;
|
||||
|
||||
@ManyToOne(() => Role, {
|
||||
eager: true,
|
||||
})
|
||||
role?: Role | null;
|
||||
|
||||
@ManyToOne(() => Status, {
|
||||
eager: true,
|
||||
})
|
||||
status?: Status;
|
||||
|
||||
@OneToMany(() => Player, (player) => player.user)
|
||||
players: Player[];
|
||||
|
||||
@Column({ nullable: true })
|
||||
@Index()
|
||||
@Exclude({ toPlainOnly: true })
|
||||
hash: string | null;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
|
||||
@DeleteDateColumn()
|
||||
deletedAt: Date;
|
||||
}
|
||||
89
myteamwallet_backend/src/users/users.controller.ts
Normal file
89
myteamwallet_backend/src/users/users.controller.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
UseGuards,
|
||||
Query,
|
||||
DefaultValuePipe,
|
||||
ParseIntPipe,
|
||||
HttpStatus,
|
||||
HttpCode,
|
||||
} from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||
import { Roles } from 'src/roles/roles.decorator';
|
||||
import { RoleEnum } from 'src/roles/roles.enum';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { RolesGuard } from 'src/roles/roles.guard';
|
||||
import { infinityPagination } from 'src/utils/infinity-pagination';
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AuthGuard('jwt'), RolesGuard)
|
||||
@ApiTags('Users')
|
||||
@Controller({
|
||||
path: 'users',
|
||||
version: '1',
|
||||
})
|
||||
export class UsersController {
|
||||
constructor(private readonly usersService: UsersService) {}
|
||||
|
||||
@Roles([RoleEnum.admin])
|
||||
@Post()
|
||||
@HttpCode(HttpStatus.CREATED)
|
||||
create(@Body() createProfileDto: CreateUserDto) {
|
||||
return this.usersService.create(createProfileDto);
|
||||
}
|
||||
|
||||
@Roles([RoleEnum.admin])
|
||||
@Get()
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async findAll(
|
||||
@Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
|
||||
@Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number,
|
||||
) {
|
||||
if (limit > 50) {
|
||||
limit = 50;
|
||||
}
|
||||
|
||||
return infinityPagination(
|
||||
await this.usersService.findManyWithPagination({
|
||||
page,
|
||||
limit,
|
||||
}),
|
||||
{ page, limit },
|
||||
);
|
||||
}
|
||||
|
||||
@Roles([RoleEnum.admin])
|
||||
@Get(':id')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.usersService.findOne({ id: +id });
|
||||
}
|
||||
|
||||
@Roles([RoleEnum.user, RoleEnum.admin])
|
||||
@Get(':id/teams')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
findTeamsOfPlayer(@Param('id') id: string) {
|
||||
return this.usersService.findTeams({ id: +id });
|
||||
}
|
||||
|
||||
@Roles([RoleEnum.admin])
|
||||
@Patch(':id')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
update(@Param('id') id: number, @Body() updateProfileDto: UpdateUserDto) {
|
||||
return this.usersService.update(id, updateProfileDto);
|
||||
}
|
||||
|
||||
@Roles([RoleEnum.admin])
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: number) {
|
||||
return this.usersService.softDelete(id);
|
||||
}
|
||||
}
|
||||
17
myteamwallet_backend/src/users/users.module.ts
Normal file
17
myteamwallet_backend/src/users/users.module.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { UsersController } from './users.controller';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { User } from './entities/user.entity';
|
||||
import { IsExist } from 'src/utils/validators/is-exists.validator';
|
||||
import { IsNotExist } from 'src/utils/validators/is-not-exists.validator';
|
||||
import { Team } from 'src/teams/entities/team.entity';
|
||||
import { Player } from 'src/players/entities/player.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([User, Team, Player])],
|
||||
controllers: [UsersController],
|
||||
providers: [IsExist, IsNotExist, UsersService],
|
||||
exports: [UsersService],
|
||||
})
|
||||
export class UsersModule {}
|
||||
80
myteamwallet_backend/src/users/users.service.ts
Normal file
80
myteamwallet_backend/src/users/users.service.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Player } from 'src/players/entities/player.entity';
|
||||
import { EntityCondition } from 'src/utils/types/entity-condition.type';
|
||||
import { IPaginationOptions } from 'src/utils/types/pagination-options';
|
||||
import { Repository } from 'typeorm';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { User } from './entities/user.entity';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
constructor(
|
||||
@InjectRepository(User)
|
||||
private usersRepository: Repository<User>,
|
||||
@InjectRepository(Player)
|
||||
private playersRepository: Repository<Player>,
|
||||
) {}
|
||||
|
||||
create(createProfileDto: CreateUserDto) {
|
||||
return this.usersRepository.save(
|
||||
this.usersRepository.create(createProfileDto),
|
||||
);
|
||||
}
|
||||
|
||||
findManyWithPagination(paginationOptions: IPaginationOptions) {
|
||||
return this.usersRepository.find({
|
||||
skip: (paginationOptions.page - 1) * paginationOptions.limit,
|
||||
take: paginationOptions.limit,
|
||||
});
|
||||
}
|
||||
|
||||
findOne(fields: EntityCondition<User>) {
|
||||
return this.usersRepository.findOne({
|
||||
where: fields,
|
||||
});
|
||||
}
|
||||
|
||||
update(id: number, updateProfileDto: UpdateUserDto) {
|
||||
return this.usersRepository.save(
|
||||
this.usersRepository.create({
|
||||
id,
|
||||
...updateProfileDto,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async softDelete(id: number): Promise<void> {
|
||||
await this.usersRepository.softDelete(id);
|
||||
}
|
||||
|
||||
async findTeams(fields: EntityCondition<User>) {
|
||||
const user = await this.findOne(fields);
|
||||
if (!user) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const players = await this.playersRepository.find({
|
||||
where: {
|
||||
user: {
|
||||
id: user.id,
|
||||
},
|
||||
},
|
||||
relations: ['team'],
|
||||
});
|
||||
return players;
|
||||
}
|
||||
|
||||
async linkPlayerToUserId(user: User, playerId: number): Promise<boolean> {
|
||||
return new Promise<boolean>(async (resolve) => {
|
||||
const player = await this.playersRepository.findOneByOrFail({
|
||||
id: playerId,
|
||||
});
|
||||
|
||||
player.user = user;
|
||||
await this.playersRepository.save(player);
|
||||
return resolve(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user