diff --git a/backend/code/prisma/dbml/schema.dbml b/backend/code/prisma/dbml/schema.dbml index 12c231b..1edca56 100644 --- a/backend/code/prisma/dbml/schema.dbml +++ b/backend/code/prisma/dbml/schema.dbml @@ -21,7 +21,8 @@ Table users { tfaStatus Boolean [not null, default: false] left_friends friends [not null] right_friends friends [not null] - matches matches [not null] + participant1 matches [not null] + participant2 matches [not null] owned_rooms rooms [not null] roomMember room_members [not null] blocked_by blocked_friends [not null] @@ -65,6 +66,7 @@ Table matches { score2 Int gametype String participant1 users [not null] + participant2 users [not null] } Table messages { @@ -139,6 +141,8 @@ Ref: blocked_friends.blocked_id > users.userId Ref: matches.participant1Id > users.userId +Ref: matches.participant2Id > users.userId + Ref: messages.roomId > rooms.id [delete: Cascade] Ref: rooms.ownerId > users.userId diff --git a/backend/code/prisma/schema.prisma b/backend/code/prisma/schema.prisma index eb82eed..f88ae47 100644 --- a/backend/code/prisma/schema.prisma +++ b/backend/code/prisma/schema.prisma @@ -35,7 +35,8 @@ model User { tfaStatus Boolean @default(false) left_friends Friend[] @relation("from") right_friends Friend[] @relation("to") - matches Match[] @relation("participant1") + participant1 Match[] @relation("participant1") + participant2 Match[] @relation("participant2") owned_rooms Room[] @relation("owner") roomMember RoomMember[] blocked_by BlockedUsers[] @relation("blocked_by") @@ -85,6 +86,7 @@ model Match { score2 Int? gametype String? participant1 User @relation("participant1", fields: [participant1Id], references: [userId]) + participant2 User @relation("participant2", fields: [participant2Id], references: [userId]) @@map("matches") } diff --git a/backend/code/src/app.module.ts b/backend/code/src/app.module.ts index 7346ade..89a85a7 100644 --- a/backend/code/src/app.module.ts +++ b/backend/code/src/app.module.ts @@ -31,14 +31,17 @@ import { APP_GUARD } from '@nestjs/core'; ThrottlerModule.forRoot([ { ttl: 6000, - limit: 1000000, + limit: 1000000, }, ]), ], controllers: [AppController], - providers: [Gateways, { - provide: APP_GUARD, - useClass: ThrottlerGuard, - }], + providers: [ + Gateways, + { + provide: APP_GUARD, + useClass: ThrottlerGuard, + }, + ], }) export class AppModule {} diff --git a/backend/code/src/game/game.controller.ts b/backend/code/src/game/game.controller.ts index 55c8fb4..4387ede 100644 --- a/backend/code/src/game/game.controller.ts +++ b/backend/code/src/game/game.controller.ts @@ -1,5 +1,8 @@ -import { Controller, Post } from '@nestjs/common'; +import { Controller, Get, Post, Query, UseGuards } from '@nestjs/common'; import { GameService } from './game.service'; +import { AtGuard } from 'src/auth/guards/at.guard'; +import { GetCurrentUser } from 'src/auth/decorator/get_current_user.decorator'; +import { QueryOffsetDto } from 'src/friends/dto/query-ofsset-dto'; @Controller('game') export class GameController { @@ -9,4 +12,13 @@ export class GameController { startGame() { // return this.gameService.startGame(); } + + @Get('history') + @UseGuards(AtGuard) + getHistory( + @GetCurrentUser('userId') userId: string, + @Query() { offset, limit }: QueryOffsetDto, + ) { + return this.gameService.getHistory(userId, offset, limit); + } } diff --git a/backend/code/src/game/game.service.ts b/backend/code/src/game/game.service.ts index 9fb6661..28a3c4a 100644 --- a/backend/code/src/game/game.service.ts +++ b/backend/code/src/game/game.service.ts @@ -1,9 +1,11 @@ import { Injectable } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { PICTURE } from 'src/profile/dto/profile.dto'; @Injectable() export class GameService { - constructor() { + constructor(private readonly prisma: PrismaService) { // this.launchGame(); } @@ -25,4 +27,68 @@ export class GameService { } }, 1000); } + + async getHistory(userId: string, offset: number, limit: number) { + const matches = await this.prisma.match.findMany({ + skip: offset, + take: limit, + where: { + OR: [ + { + participant1Id: userId, + }, + { + participant2Id: userId, + }, + ], + }, + orderBy: { + createdAt: 'desc', + }, + select: { + createdAt: true, + score1: true, + score2: true, + participant1: { + select: { + Username: true, + avatar: true, + }, + }, + participant2: { + select: { + Username: true, + avatar: true, + }, + }, + }, + }); + return matches.map((match) => { + const avatar1: PICTURE = { + thumbnail: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_48,w_48/${match.participant1.avatar}`, + medium: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_72,w_72/${match.participant1.avatar}`, + large: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_128,w_128/${match.participant1.avatar}`, + }; + const avatar2: PICTURE = { + thumbnail: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_48,w_48/${match.participant2.avatar}`, + medium: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_72,w_72/${match.participant2.avatar}`, + large: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_128,w_128/${match.participant2.avatar}`, + }; + return { + match: { + createdAt: match.createdAt, + Player1: { + username: match.participant1.Username, + score: match.score1, + avatar: avatar1, + }, + Player2: { + username: match.participant2.Username, + score: match.score2, + avatar: avatar2, + }, + }, + }; + }); + } } diff --git a/backend/code/src/rooms/rooms.controller.ts b/backend/code/src/rooms/rooms.controller.ts index dd11f12..f2801ab 100644 --- a/backend/code/src/rooms/rooms.controller.ts +++ b/backend/code/src/rooms/rooms.controller.ts @@ -167,6 +167,16 @@ export class RoomsController { return await this.roomsService.banMember(memberData, userId); } + @Post('unban') + @HttpCode(HttpStatus.OK) + @UseGuards(AtGuard) + async unbanMember( + @Body() memberData: ChangeOwnerDto, + @GetCurrentUser('userId') userId: string, + ) { + return await this.roomsService.unbanMember(memberData, userId); + } + @Post('add') @HttpCode(HttpStatus.OK) @UseGuards(AtGuard) diff --git a/backend/code/src/rooms/rooms.service.ts b/backend/code/src/rooms/rooms.service.ts index 7518de8..798f1c6 100644 --- a/backend/code/src/rooms/rooms.service.ts +++ b/backend/code/src/rooms/rooms.service.ts @@ -221,7 +221,7 @@ export class RoomsService { if (room.ownerId !== userId) throw new UnauthorizedException('You are not the owner of this room'); if (!member) - throw new UnauthorizedException('new owner is not a member of this room'); + throw new UnauthorizedException('user is not a member of this room'); await this.prisma.room.update({ where: { id: roomData.roomId }, data: { owner: { connect: { userId: roomData.memberId } } }, @@ -251,7 +251,7 @@ export class RoomsService { if (room.ownerId !== userId) throw new UnauthorizedException('You are not the owner of this room'); if (!user) - throw new UnauthorizedException('new admin is not a member of this room'); + throw new UnauthorizedException('user is not a member of this room'); if (user.is_admin || room.ownerId === roomData.memberId) throw new UnauthorizedException( 'new admin is already an admin of this room', @@ -334,6 +334,11 @@ export class RoomsService { roomId: roomData.roomId, }, }, + select: { + room: true, + is_mueted: true, + userId: true, + }, }); if (!room) throw new HttpException('room not found', HttpStatus.NOT_FOUND); if (!member) @@ -342,6 +347,8 @@ export class RoomsService { throw new UnauthorizedException('You are not admin of this room'); if (member.is_mueted) throw new UnauthorizedException('member is already muted'); + if (member.room.ownerId === roomData.memberId) + throw new UnauthorizedException('You cannot mute the owner'); if (member.userId === userId) throw new UnauthorizedException('You can not mute yourself'); const afterFiveMin = new Date(Date.now() + 5 * 60 * 1000); @@ -382,42 +389,40 @@ export class RoomsService { const user = await this.prisma.roomMember.findUnique({ where: { unique_user_room: { userId: userId, roomId: roomId } }, }); - if (!user) throw new UnauthorizedException('You are not a member of this room'); - const members = await this.prisma.roomMember.findMany({ skip: offset, take: limit, where: { roomId: roomId, - is_banned: false, }, select: { user: { select: { - userId: true, firstName: true, lastName: true, avatar: true, }, }, + is_banned: true, }, }); return members.map((member) => { - const avatar: PICTURE = { - thumbnail: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_48,w_48/${member.user.avatar}`, - medium: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_72,w_72/${member.user.avatar}`, - large: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_128,w_128/${member.user.avatar}`, - }; - return { - id: member.user.userId, - name: { first: member.user.firstName, last: member.user.lastName }, - avatar, + if (!member.is_banned || user.is_admin) { + const avatar: PICTURE = { + thumbnail: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_48,w_48/${member.user.avatar}`, + medium: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_72,w_72/${member.user.avatar}`, + large: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_128,w_128/${member.user.avatar}`, + }; + return { + firstname: member.user.firstName, + lastname: member.user.lastName, + avatar: avatar, + }; } }); } - async banMember(memberData: ChangeOwnerDto, userId: string) { const user = await this.prisma.roomMember.findUnique({ where: { @@ -433,8 +438,8 @@ export class RoomsService { if (userId == memberData.memberId) throw new UnauthorizedException('You cannot ban yourself'); if (ownerId == memberData.memberId) - throw new UnauthorizedException('You cannot ban the Owner of thi Room'); - return await this.prisma.roomMember.update({ + throw new UnauthorizedException('You cannot ban the Owner of this Room'); + await this.prisma.roomMember.update({ where: { unique_user_room: { userId: memberData.memberId, @@ -446,6 +451,41 @@ export class RoomsService { bannedAt: new Date(Date.now()), }, }); + return { message: 'member banned successfully' }; + } + + async unbanMember(memberData: ChangeOwnerDto, userId: string) { + const user = await this.prisma.roomMember.findUnique({ + where: { + unique_user_room: { userId: userId, roomId: memberData.roomId }, + }, + }); + const member = await this.prisma.roomMember.findUnique({ + where: { + unique_user_room: { + userId: memberData.memberId, + roomId: memberData.roomId, + }, + }, + }); + if (!member) + throw new HttpException('user not found', HttpStatus.NOT_FOUND); + if (!member.is_banned) + throw new HttpException('member is not banned', HttpStatus.BAD_REQUEST); + if (!user.is_admin) + throw new UnauthorizedException('You are not admin of this room'); + await this.prisma.roomMember.update({ + where: { + unique_user_room: { + userId: memberData.memberId, + roomId: memberData.roomId, + }, + }, + data: { + is_banned: false, + }, + }); + return { message: 'member unbanned successfully' }; } async addMember(memberData: ChangeOwnerDto, userId: string) { @@ -498,18 +538,19 @@ export class RoomsService { name: true, type: true, ownerId: true, - ...(joined && {members: { - where: { - userId: userId, - }, - select: { - is_admin: true, + ...(joined && { + members: { + where: { + userId: userId, + }, + select: { + is_admin: true, + }, }, - }}) + }), }, }); - if (!joined) - return rooms; + if (!joined) return rooms; return rooms.map((room) => { const is_owner = room.ownerId === userId; return {