From 89ced4319b5ef2aaffce9e0d223ea9f94f7cd58d Mon Sep 17 00:00:00 2001 From: nicolas-arnaud Date: Wed, 15 Mar 2023 08:50:52 +0100 Subject: [PATCH] ready to try channels management --- back/volume/src/chat/chat.controller.ts | 146 ++++++++++++------ back/volume/src/chat/chat.service.ts | 24 ++- back/volume/src/chat/dto/updateUser.dto.ts | 16 ++ back/volume/src/chat/entity/channel.entity.ts | 2 +- front/volume/src/components/Channels.svelte | 2 +- 5 files changed, 133 insertions(+), 57 deletions(-) create mode 100644 back/volume/src/chat/dto/updateUser.dto.ts diff --git a/back/volume/src/chat/chat.controller.ts b/back/volume/src/chat/chat.controller.ts index accdd58..f43d86e 100644 --- a/back/volume/src/chat/chat.controller.ts +++ b/back/volume/src/chat/chat.controller.ts @@ -15,7 +15,7 @@ import { UsersService } from 'src/users/users.service' import { ChannelService } from './chat.service' import { CreateChannelDto } from './dto/create-channel.dto' -import { UpdateChannelDto } from './dto/update-channel.dto' +import { IdDto, PasswordDto, MuteDto } from './dto/updateUser.dto' import type User from 'src/users/entity/user.entity' import type Channel from './entity/channel.entity' @@ -30,66 +30,113 @@ export class ChatController { ) {} @Post(':id/invite') - async addUser (@Param('id') id: number, @Body() userId: number) { - const channel = await this.channelService.getChannel(id) - const user: User | null = await this.usersService.findUser(userId) - if (user == null) throw new NotFoundException(`User #${userId} not found`) + @UseGuards(AuthenticatedGuard) + async addUser ( + @Param('id') id: number, @Body() target: IdDto, + @Profile42() profile: Profile) { + const channel = await this.channelService.getFullChannel(id) + const user: User | null = await this.usersService.findUser(target.id) + if (user == null) throw new NotFoundException(`User #${target.id} not found`) + if (!await this.channelService.isUser(channel.id, +profile.id)) + throw new BadRequestException('You are not allowed to invite users to this channel') + if (await this.channelService.isUser(channel.id, target.id)) + throw new BadRequestException('User is already in this channel') + if (await this.channelService.isBanned(channel.id, target.id)) + throw new BadRequestException('User is banned from this channel') channel.users.push(user) - this.channelService.update(channel) + this.channelService.save(channel) } @Delete(':id/kick') - async removeUser (@Param('id') id: number, @Body() userId: number) { - const channel = await this.channelService.getChannel(id) - channel.users = channel.admins.filter((usr: User) => { - return usr.ftId !== userId + @UseGuards(AuthenticatedGuard) + async removeUser ( + @Param('id') id: number, @Body() target: IdDto, + @Profile42() profile: Profile) { + const channel = await this.channelService.getFullChannel(id) + if (!await this.channelService.isAdmin(channel.id, +profile.id)) + throw new BadRequestException('You are not allowed to kick users from this channel') + if (!await this.channelService.isUser(channel.id, target.id)) + throw new BadRequestException('User is not in this channel') + if (await this.channelService.isOwner(channel.id, target.id)) + throw new BadRequestException('You cannot kick the owner of the channel') + channel.users = channel.users.filter((usr: User) => { + return usr.ftId !== target.id }) - this.channelService.update(channel) + this.channelService.save(channel) } @Post(':id/admin') - async addAdmin (@Param('id') id: number, @Body() userId: number) { - const channel = await this.channelService.getChannel(id) - const user: User | null = await this.usersService.findUser(userId) - if (user == null) throw new NotFoundException(`User #${userId} not found`) + @UseGuards(AuthenticatedGuard) + async addAdmin ( + @Param('id') id: number, + @Body() target: IdDto, + @Profile42() profile: Profile) { + const channel = await this.channelService.getFullChannel(id) + const user: User | null = await this.usersService.findUser(target.id) + if (user == null) throw new NotFoundException(`User #${target.id} not found`) + if (!await this.channelService.isOwner(channel.id, +profile.id)) + throw new BadRequestException('You are not the owner of this channel') + if (!await this.channelService.isUser(channel.id, target.id)) + throw new BadRequestException('User is not in this channel') + if (await this.channelService.isAdmin(channel.id, target.id)) + throw new BadRequestException('User is already an admin of this channel') channel.admins.push(user) - this.channelService.update(channel) + this.channelService.save(channel) } @Delete(':id/admin') - async removeAdmin (@Param('id') id: number, @Body() userId: number) { - const channel = await this.channelService.getChannel(id) + @UseGuards(AuthenticatedGuard) + async removeAdmin ( + @Param('id') id: number, + @Body() target: IdDto, + @Profile42() profile: Profile) { + const channel = await this.channelService.getFullChannel(id) + if (!await this.channelService.isOwner(channel.id, +profile.id)) + throw new BadRequestException('You are not the owner of this channel') + if (!await this.channelService.isAdmin(channel.id, target.id)) + throw new BadRequestException('User is not an admin of this channel') channel.admins = channel.admins.filter((usr: User) => { - return usr.ftId !== userId + return usr.ftId !== target.id }) - this.channelService.update(channel) + this.channelService.save(channel) } @Post(':id/ban') - async addBan (@Param('id') id: number, @Body() userId: number) { - const channel = await this.channelService.getChannel(id) - const user: User | null = await this.usersService.findUser(userId) - if (user == null) throw new NotFoundException(`User #${userId} not found`) + @UseGuards(AuthenticatedGuard) + async addBan ( + @Param('id') id: number, + @Body() target: IdDto, + @Profile42() profile: Profile) { + const channel = await this.channelService.getFullChannel(id) + const user: User | null = await this.usersService.findUser(target.id) + if (user == null) throw new NotFoundException(`User #${target.id} not found`) + if (!await this.channelService.isAdmin(channel.id, +profile.id)) + throw new BadRequestException('You are not allowed to ban users from this channel') + if (await this.channelService.isOwner(channel.id, target.id)) + throw new BadRequestException('You cannot ban the owner of the channel') + if (await this.channelService.isBanned(channel.id, target.id)) + throw new BadRequestException('User is already banned from this channel') channel.banned.push(user) - this.channelService.update(channel) + this.channelService.save(channel) } @Post(':id/mute') - async addMute (@Param('id') id: number, @Body() userId: number) { - const channel = await this.channelService.getChannel(id) - const user: User | null = await this.usersService.findUser(userId) - if (user == null) throw new NotFoundException(`User #${userId} not found`) - channel.muted.push(user) - this.channelService.update(channel) - } - - @Delete(':id/mute') - async removeMute (@Param('id') id: number, @Body() userId: number) { - const channel = await this.channelService.getChannel(id) - channel.muted = channel.muted.filter((usr: User) => { - return usr.ftId !== userId - }) - this.channelService.update(channel) + @UseGuards(AuthenticatedGuard) + async addMute ( + @Param('id') id: number, + @Body() mute: MuteDto, // [userId, duration] + @Profile42() profile: Profile) { + const channel = await this.channelService.getFullChannel(id) + const user: User | null = await this.usersService.findUser(mute.data[0]) + if (user == null) throw new NotFoundException(`User #${mute.data[0]} not found`) + if (!await this.channelService.isAdmin(channel.id, +profile.id)) + throw new BadRequestException('You are not allowed to mute users from this channel') + if (await this.channelService.isOwner(channel.id, mute.data[0])) + throw new BadRequestException('You cannot mute the owner of the channel') + if (await this.channelService.getMuteDuration(channel.id, mute.data[0]) > 0) + throw new BadRequestException('User is already muted from this channel') + channel.muted.push(mute.data) + this.channelService.save(channel) } @Delete(':id') @@ -98,11 +145,10 @@ export class ChatController { @Profile42() profile: Profile, @Param('id') id: number ) { - if (await this.channelService.isOwner(id, +profile.id)) { - await this.channelService.removeChannel(id) - return - } - throw new BadRequestException('You are not the owner of this channel') + if (!await this.channelService.isOwner(id, +profile.id)) + throw new BadRequestException('You are not the owner of this channel') + await this.channelService.removeChannel(id) + return } @Post(':id/password') @@ -110,13 +156,11 @@ export class ChatController { async updatePassword ( @Profile42() profile: Profile, @Param('id') id: number, - @Body() password: string + @Body() data: PasswordDto ) { - if (await this.channelService.isOwner(id, +profile.id)) { - await this.channelService.updatePassword(id, password) - return - } - throw new BadRequestException('You are not the owner of this channel') + if (await this.channelService.isOwner(id, +profile.id)) + throw new BadRequestException('You are not the owner of this channel') + await this.channelService.updatePassword(id, data.password) } diff --git a/back/volume/src/chat/chat.service.ts b/back/volume/src/chat/chat.service.ts index 29eea35..1ee459d 100644 --- a/back/volume/src/chat/chat.service.ts +++ b/back/volume/src/chat/chat.service.ts @@ -7,7 +7,6 @@ import { UsersService } from 'src/users/users.service' import type User from 'src/users/entity/user.entity' import Channel from './entity/channel.entity' -import { classToPlain, plainToClass } from 'class-transformer' @Injectable() export class ChannelService { @@ -68,8 +67,21 @@ export class ChannelService { return channel } + async getFullChannel (id: number): Promise { + const channel = await this.ChannelRepository.findOne({ + where: { id }, + relations: ['users', 'admins', 'banned', 'muted', 'owner'] + }) + if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) } + return channel + } + async update (channel: Channel) { - this.ChannelRepository.update(channel.id, channel) + await this.ChannelRepository.update(channel.id, channel) + } + + async save (channel: Channel) { + await this.ChannelRepository.save(channel) } async removeChannel (channelId: number) { @@ -112,12 +124,16 @@ export class ChannelService { return channel.banned.findIndex((user) => user.ftId === userId) != -1 } - async isMuted (id: number, userId: number): Promise { + async getMuteDuration (id: number, userId: number): Promise { const channel = await this.ChannelRepository.findOne({ where: { id }, relations: { muted: true } }) if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) } - return channel.muted.findIndex((user) => user.ftId === userId) != -1 + + const mutation: Array | undefined = channel.muted.find((mutation) => mutation[0] === userId) + if (mutation == null) { return 0 } + return mutation[1] + } } diff --git a/back/volume/src/chat/dto/updateUser.dto.ts b/back/volume/src/chat/dto/updateUser.dto.ts new file mode 100644 index 0000000..bbc0350 --- /dev/null +++ b/back/volume/src/chat/dto/updateUser.dto.ts @@ -0,0 +1,16 @@ + +import { IsNumber, IsString} from 'class-validator' + +export class IdDto { + @IsNumber() + id: number +} + +export class PasswordDto { + @IsString() + password: string +} + +export class MuteDto { + data: Array +} diff --git a/back/volume/src/chat/entity/channel.entity.ts b/back/volume/src/chat/entity/channel.entity.ts index f2b51c9..475e0a9 100644 --- a/back/volume/src/chat/entity/channel.entity.ts +++ b/back/volume/src/chat/entity/channel.entity.ts @@ -57,5 +57,5 @@ export default class Channel { @ManyToMany(() => User) // refuse post @JoinTable() - muted: User[] + muted: Array> } diff --git a/front/volume/src/components/Channels.svelte b/front/volume/src/components/Channels.svelte index 95ff953..eb2e543 100644 --- a/front/volume/src/components/Channels.svelte +++ b/front/volume/src/components/Channels.svelte @@ -94,7 +94,7 @@ }); if (response.ok) { const user = await response.json(); - const response2 = await fetch(API_URL + "/channels/" + id, { + const response2 = await fetch(API_URL + "/channels/" + id + "/invite", { credentials: "include", method: "POST", mode: "cors",