diff --git a/Makefile b/Makefile index 10e8bccb..27e3080f 100644 --- a/Makefile +++ b/Makefile @@ -24,5 +24,15 @@ destroy: - docker images -aq | xargs --no-run-if-empty docker rmi -f - docker volume ls -q | xargs --no-run-if-empty docker volume rm +# temp for hugo, only reinit database +db: + - docker rm -f postgresql + - docker rm -f nestjs + - docker volume rm -f srcs_data_nest_postgresql + docker compose -f ${DOCKERCOMPOSEPATH} up -d --build + @make start + @docker ps + + stop: docker compose -f ${DOCKERCOMPOSEPATH} stop diff --git a/README.md b/README.md index 45ffa171..c952c5ab 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -CONFLICT srcs/requirements/nestjs/api_back/src/friendship/friendship.service.ts ### Pour lancer le docker : @@ -69,15 +68,25 @@ CONFLICT srcs/requirements/nestjs/api_back/src/friendship/friendship.service.ts #### chat : -- [ ] can create chat-rooms (public/private, password protected) -- [ ] send direct messages -- [ ] block other users -- [ ] creators of chat-room are owners, untill they leave -- [ ] chat-room owner can set, change, remove password -- [ ] chat-room owner is administrator and can set other administrators -- [ ] administrators can ban or mute for a time other users -- [ ] send game invitation in chat -- [ ] view user profiles from chat +- [/] create public room +- [ ] create private room +- [/] create direct room +- [/] chat in room +- [/] join public rooms +- [ ] join private rooms +- [ ] join direct rooms +- [/] see all joignable rooms +- [/] see all my rooms +- [/] leave room +- [ ] leave direct +- [ ] invite someone in room +- [ ] make admin +- [ ] ban +- [ ] mute +- [ ] protect room with password +- [ ] bock users +- [ ] send game invitation +- [ ] view user profiles #### game : diff --git a/make_env.sh b/make_env.sh index 7c218de4..35516df3 100755 --- a/make_env.sh +++ b/make_env.sh @@ -35,6 +35,7 @@ RESET="\033[0m" function make_env_for_docker_and_svelte { docker rm -f postgresql + docker rm -f nestjs docker volume rm -f srcs_data_nest_postgresql echo -e "${BOLD_BLUE}Creating a new environment for docker${RESET}" NODE_ENV="" diff --git a/srcs/requirements/nestjs/api_back/src/auth/42/authentication.controller.ts b/srcs/requirements/nestjs/api_back/src/auth/42/authentication.controller.ts index 44de4932..b255592f 100644 --- a/srcs/requirements/nestjs/api_back/src/auth/42/authentication.controller.ts +++ b/srcs/requirements/nestjs/api_back/src/auth/42/authentication.controller.ts @@ -5,6 +5,7 @@ import { Response } from 'express'; import { TwoFaDto } from './dto/2fa.dto'; import { UsersService } from 'src/users/users.service'; import { User } from 'src/users/entities/user.entity'; +import { STATUS } from 'src/common/constants/constants'; @Controller('auth') export class AuthenticationController { @@ -36,6 +37,7 @@ export class AuthenticationController { console.log('On redirige'); const user : User = request.user if (user.isEnabledTwoFactorAuth === false || user.isTwoFactorAuthenticated === true){ + this.userService.updateStatus(user.id, STATUS.CONNECTED) console.log('ON VA VERS PROFILE'); return response.status(200).redirect('http://' + process.env.WEBSITE_HOST + ':' + process.env.WEBSITE_PORT + '/#/profile'); } @@ -51,7 +53,7 @@ export class AuthenticationController { @UseGuards(AuthenticateGuard) logout(@Req() request, @Res() response, @Next() next) { this.userService.setIsTwoFactorAuthenticatedWhenLogout(request.user.id); - this.userService.updateStatus(request.user.id, 'disconnected'); + this.userService.updateStatus(request.user.id, STATUS.DISCONNECTED); request.logout(function(err) { if (err) { return next(err); } response.redirect('/'); @@ -83,6 +85,7 @@ export class AuthenticationController { throw new UnauthorizedException('Wrong Code.'); await this.userService.authenticateUserWith2FA(request.user.id); console.log('ON REDIRIGE'); + this.userService.updateStatus(user.id, STATUS.CONNECTED) return response.status(200).redirect('http://' + process.env.WEBSITE_HOST + ':' + process.env.WEBSITE_PORT + '/#/profile'); } } diff --git a/srcs/requirements/nestjs/api_back/src/chat/chat.controller.ts b/srcs/requirements/nestjs/api_back/src/chat/chat.controller.ts index 149f9893..12c8a9b1 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/chat.controller.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/chat.controller.ts @@ -1,84 +1,147 @@ -import { Controller, UseGuards, HttpException, HttpStatus, Get, Post, Body, Req, Res } from '@nestjs/common'; +import { Controller, UseGuards, HttpException, HttpStatus, Get, Post, Delete, Body, Req, Res } from '@nestjs/common'; import { AuthenticateGuard, TwoFactorGuard } from 'src/auth/42/guards/42guards'; import { ConnectedSocket } from '@nestjs/websockets'; import { ChatService } from './chat.service'; import { User } from 'src/users/entities/user.entity'; import { PartialUsersDto } from 'src/users/dto/partial-users.dto'; -import { createRoomDto } from './dto/createRoom.dto'; -import { joinRoomDto } from './dto/joinRoom.dto'; +import { roomDto } from './dto/room.dto'; import { setCurrentRoomDto } from './dto/setCurrentRoom.dto'; +import { ChatGateway } from './chat.gateway'; +import { socketDto } from './dto/socket.dto'; +import { Chatroom } from './entities/chatroom.entity'; @Controller('chat') export class ChatController { constructor( private chatService: ChatService, + private chatGateway: ChatGateway, ) {} + // don't allow '+' because it's used in direct rooms name + private allowed_chars = '-#!?_'; + private escape_chars(str) + { + return str.split("").join("\\"); + } + + @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Get('myrooms') - async getMyRooms(@Req() req, @Res() res): Promise + async getMyRooms(@Req() req, @Res() res): Promise { console.log("- in getMyRooms controller"); - const rooms = await this.chatService.getMyRooms(req.user); + + let fields = ["name", "type", "users"]; + const rooms = await this.chatService.getMyRooms(req.user.username, fields); + + res.status(HttpStatus.OK).json({ rooms: rooms }); + console.log("- out getMyRooms controller"); - return res.status(HttpStatus.OK).json({ rooms: rooms }); } @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Get('allrooms') - async getAllRooms(@Req() req, @Res() res): Promise + async getAllRooms(@Req() req, @Res() res): Promise { console.log("- in getAllRooms controller"); - const rooms = await this.chatService.getAllNotMyRooms(req.user); + + const rooms: roomDto[] = await this.chatService.getAllOtherRoomsAndUsers(req.user.username) + console.log("--- rooms:", rooms); + res.status(HttpStatus.OK).json({ rooms: rooms }); + console.log("- out getAllRooms controller"); - return res.status(HttpStatus.OK).json({ rooms: rooms }); } @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Get('current') - async setCurrentRoom(@Body() setCurrentRoomDto: setCurrentRoomDto, @Req() req, @Res() res): Promise + async setCurrentRoom(@Body() setCurrentRoomDto: setCurrentRoomDto, @Req() req, @Res() res): Promise { console.log("- in setCurrentRoom controller"); - const response = await this.chatService.setCurrentRoom(req.user, setCurrentRoomDto.name); + + const response = await this.chatService.setCurrentRoom(req.user.username, setCurrentRoomDto.name); + res.status(HttpStatus.OK).json({ message: response }); + console.log("- out setCurrentRoom controller"); - return res.status(HttpStatus.OK).json({ message: response }); + } + + @UseGuards(AuthenticateGuard) + @UseGuards(TwoFactorGuard) + @Get('allowedchars') + async allowedChars(@Res() res): Promise + { + console.log("- in allowedChars controller"); + + res.status(HttpStatus.OK).json({ chars: this.allowed_chars }); + + console.log("- out allowedChars controller"); } @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Post('create') - async createRoom(@Body() createRoomDto: createRoomDto, @Req() req, @Res() res): Promise + async createRoom(@Body() room: roomDto, @Req() req, @Res() res): Promise { console.log("- in createRoom controller"); - const response = await this.chatService.addUserToNewRoom(req.user, createRoomDto); + + let chars = this.escape_chars(this.allowed_chars); + let regex_base = `[a-zA-Z0-9\\s${chars}]`; + let test_regex = new RegExp(`^${regex_base}+$`); + if (test_regex.test(room.name) === false) + { + let forbidden_chars = room.name.replace(new RegExp(regex_base, "g"), ""); + throw new HttpException(`Your room name can not contains these characters : ${forbidden_chars}`, HttpStatus.UNPROCESSABLE_ENTITY); + } + + await this.chatService.addUserToNewRoom(req.user.username, room); + res.status(HttpStatus.OK).json({ room: room }); + console.log("- out createRoom controller"); - return res.status(HttpStatus.OK).json({ room_name: createRoomDto.room_name, message: response }); } @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Post('join') - async joinRoom(@Body() joinRoomDto: joinRoomDto, @Req() req, @Res() res): Promise + async joinRoom(@Body() room: roomDto, @Req() req, @Res() res): Promise { console.log("- in joinRoom controller"); - const response = await this.chatService.addUserToRoom(req.user, joinRoomDto); + + let response = ""; + if (room.type === 'direct') + throw new HttpException(`cannot join a direct messages room`, HttpStatus.CONFLICT); + else if (room.type === 'user') + { + room.type = 'direct'; + room.users = [room.name, req.user.username]; + room.name += ` + ${req.user.username}`; + await this.chatService.addUserToNewRoom(req.user.username, room); + } + else + await this.chatService.addUserToRoom(req.user.username, room.name); + + let socket: socketDto = this.chatGateway.sockets.get(req.user.username); + await this.chatService.socketJoinRoom(socket, room.name); + res.status(HttpStatus.OK).json({ room: room }); + console.log("- out joinRoom controller"); - return res.status(HttpStatus.OK).json({ room_name: joinRoomDto.room_name, message: response }); } @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Post('change') - async changeRoom(@Body() joinRoomDto: joinRoomDto, @Req() req, @Res() res): Promise + async changeRoom(@Body() room: roomDto, @Req() req, @Res() res): Promise { console.log("- in changeRoom controller"); - const response = await this.chatService.setCurrentRoom(req.user, joinRoomDto.room_name); + + const response = await this.chatService.setCurrentRoom(req.user.username, room.name); + let socket: socketDto = this.chatGateway.sockets.get(req.user.username); + await this.chatService.socketChangeRoom(socket, room.name); + res.status(HttpStatus.OK).json({ room: room }); + console.log("- out changeRoom controller"); - return res.status(HttpStatus.OK).json({ room_name: joinRoomDto.room_name, message: response }); } @UseGuards(AuthenticateGuard) @@ -93,12 +156,43 @@ export class ChatController { @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) @Get('messages') - async getMessages(@Req() req, @Res() res): Promise + async getMessages(@Req() req, @Res() res): Promise { console.log("- in getMessages controller"); - const messages = await this.chatService.getMessagesFromCurrentRoom(req.user); + + const messages = await this.chatService.getMessagesFromCurrentRoom(req.user.username); + res.status(HttpStatus.OK).json({ messages: messages }); + console.log("- out getMessages controller"); - return res.status(HttpStatus.OK).json({ messages: messages }); + } + + @UseGuards(AuthenticateGuard) + @UseGuards(TwoFactorGuard) + @Get('roomusers') + async getRoomUsers(@Req() req, @Res() res): Promise + { + console.log("- in getRoomUsers controller"); + + const room_name = await this.chatService.getCurrentRoomName(req.user.username); + const room = await this.chatService.getRoomByName(room_name); + const users = room.users; + res.status(HttpStatus.OK).json({ users: users }); + + console.log("- out getRoomUsers controller"); + } + + @UseGuards(AuthenticateGuard) + @UseGuards(TwoFactorGuard) + @Delete('removeuser') + async removeUser(@Req() req, @Res() res): Promise + { + console.log("- in removeUser controller"); + + const room_name = await this.chatService.getCurrentRoomName(req.user.username); + let response = await this.chatService.removeUserFromRoom(req.user.username, room_name); + res.status(HttpStatus.OK).json({ message: response }); + + console.log("- out removeUser controller"); } } diff --git a/srcs/requirements/nestjs/api_back/src/chat/chat.gateway.ts b/srcs/requirements/nestjs/api_back/src/chat/chat.gateway.ts index 5b6e257b..b3963f1b 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/chat.gateway.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/chat.gateway.ts @@ -1,14 +1,14 @@ import { WebSocketGateway, SubscribeMessage, WebSocketServer, MessageBody, ConnectedSocket, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets'; import { UsersService } from 'src/users/users.service'; -import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto'; import { ChatService } from './chat.service'; +import { socketDto } from './dto/socket.dto'; @WebSocketGateway(5000, { path: '/chat', }) export class ChatGateway - implements OnGatewayConnection, OnGatewayDisconnect +implements OnGatewayConnection, OnGatewayDisconnect { constructor ( @@ -16,40 +16,44 @@ export class ChatGateway private chatService: ChatService, ) {} + sockets = new Map(); + @WebSocketServer() server; - - // how to guard the handleConnection ? - // https://github.com/nestjs/nest/issues/882 - async handleConnection(client) { - console.log('- Client connected :', client.id, client.handshake.query.username); - client.username = client.handshake.query.username; + async handleConnection(socket: socketDto) { + console.log('- socket connected :', socket.id, socket.handshake.query.username); + socket.username = socket.handshake.query.username.toString(); + this.sockets.set(socket.username, socket); } - async handleDisconnect(client) { - console.log('- Client disconnected :', client.id, client.username); + async handleDisconnect(socket: socketDto) { + this.sockets.delete(socket.username); } @SubscribeMessage('join') - async joinRoom(@ConnectedSocket() socket, @MessageBody() room_name: string): Promise + async joinRoom(@ConnectedSocket() socket: socketDto, @MessageBody() room_name: string): Promise { console.log('- in joinRoom gateway'); socket.leave(socket.room); socket.join(room_name); socket.room = room_name; + let message = `${socket.username} has join the room`; + await socket.to(socket.room).emit('message', "SERVER", message); + await this.chatService.addMessageToRoom(room_name, "SERVER", message); + } - console.log('- out joinRoom gateway'); + @SubscribeMessage('change') + async changeRoom(@ConnectedSocket() socket: socketDto, @MessageBody() room_name: string): Promise + { + console.log('- in changeRoom gateway'); + await this.chatService.socketChangeRoom(socket, room_name); } @SubscribeMessage('message') - async handleMessage(@ConnectedSocket() socket, @MessageBody() message: string): Promise + async handleMessage(@ConnectedSocket() socket: socketDto, @MessageBody() message: string): Promise { console.log('- in handleMessage gateway'); - //let room_name = await this.chatService.getCurrentRoom(socket.username); - socket.to(socket.room).emit('message', socket.username, message); - this.chatService.addMessageToCurrentRoom(socket.username, message); - - console.log('- out handleMessage gateway'); + await this.chatService.socketIncommingMessage(socket, message); } } diff --git a/srcs/requirements/nestjs/api_back/src/chat/chat.module.ts b/srcs/requirements/nestjs/api_back/src/chat/chat.module.ts index 326abb7c..1ab18c9d 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/chat.module.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/chat.module.ts @@ -3,7 +3,6 @@ import { ChatController } from './chat.controller'; import { ChatService } from './chat.service'; import { ChatGateway } from './chat.gateway'; import { UsersModule } from 'src/users/users.module'; - import { TypeOrmModule } from '@nestjs/typeorm'; import { Chatroom } from './entities/chatroom.entity'; import { User } from 'src/users/entities/user.entity'; diff --git a/srcs/requirements/nestjs/api_back/src/chat/chat.service.ts b/srcs/requirements/nestjs/api_back/src/chat/chat.service.ts index 52ade59f..a59720db 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/chat.service.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/chat.service.ts @@ -4,9 +4,10 @@ import { UsersService } from 'src/users/users.service'; import { Chatroom } from './entities/chatroom.entity'; import { Repository } from 'typeorm'; import { InjectRepository } from '@nestjs/typeorm'; -import { createRoomDto } from './dto/createRoom.dto'; -import { joinRoomDto } from './dto/joinRoom.dto'; +import { roomDto } from './dto/room.dto'; import { messagesDto } from './dto/messages.dto'; +import { socketDto } from './dto/socket.dto'; + @Injectable() export class ChatService { @@ -17,8 +18,7 @@ export class ChatService { private readonly userRepository: Repository, @InjectRepository(Chatroom) private readonly chatroomRepository: Repository, - ) { } - + ) {} // temp for test sleep(ms) { @@ -29,86 +29,140 @@ export class ChatService { /* GETTERS ************************************************ */ - async getMyRooms(user: User) + async getMyRooms(username: string, fieldsToReturn: string[] = null): Promise { console.log("-- in getMyRooms service"); - const rooms = await this.chatroomRepository + + const queryBuilder = this.chatroomRepository .createQueryBuilder('chatroom') - .where('chatroom.users LIKE :user_name', { user_name: `%${user.username}%` }) - .getMany(); + .where('chatroom.users LIKE :user_name', { user_name: `%${username}%` }); + + if (fieldsToReturn) + { + let fields = fieldsToReturn.map(field => `chatroom.${field}`); + queryBuilder.select(fields); + } + + const rooms = await queryBuilder.getMany(); + console.log("--- rooms:", rooms); console.log("-- out getMyRooms service"); return rooms; } - async getAllRooms() + async getMyDirects(username: string): Promise + { + console.log("-- in getAllNotMyRooms service"); + + const my_rooms = await this.getMyRooms(username); + const directs = my_rooms.filter(room => room.type === 'direct'); + + console.log("-- out getAllNotMyRooms service"); + return directs; + } + + async getAllRooms(): Promise { console.log("-- in getAllRooms service"); + const rooms = await this.chatroomRepository .createQueryBuilder('chatroom') .getMany(); + console.log("--- rooms:", rooms); console.log("-- out getAllRooms service"); return rooms; } - async getAllNotMyRooms(user: User) + async getAllNotMyRooms(username: string): Promise { console.log("-- in getAllNotMyRooms service"); - const user_db = await this.getUserByName(user.username); - //const user_db = await this.usersService.findOne(user.username); + + const user_db = await this.getUserByName(username); const rooms = await this.chatroomRepository .createQueryBuilder('chatroom') .where('chatroom.type != :type', { type: 'private' }) - .andWhere('chatroom.users NOT LIKE :user_name', { user_name: `%${user.username}%` }) + .andWhere('chatroom.users NOT LIKE :user_name', { user_name: `%${username}%` }) .getMany(); - //const users = await this.getAllUsers(); - //let allRooms = [...rooms, ...users]; + console.log("--- rooms:", rooms); console.log("-- out getAllNotMyRooms service"); return rooms; } - async getMessagesFromCurrentRoom(user: User) + async getAllOtherRoomsAndUsers(username: string): Promise { - console.log("-- in getMessagesFromCurrentRoom service"); - const user_db = await this.getUserByName(user.username); - //const user_db = await this.usersService.findOne(user.username); - const currentRoom = await this.getRoomByName(user_db.currentRoom); + console.log("-- in getAllOtherRoomsAndUsers service"); - console.log("-- out getMessagesFromCurrentRoom service"); - return currentRoom.messages; + const all_rooms = await this.getAllNotMyRooms(username); + const all_users = await this.getAllUsersNotMyRooms(username); + + let row_rooms = all_rooms.map(room => { + return { + name: room.name, + type: room.type, + }; + }); + let users = all_users.map(user => { + return { + name: user.username, + type: "user", + }; + }); + let rooms = row_rooms.concat(users); + console.log("--- rooms:", rooms); + + console.log("-- in getAllOtherRoomsAndUsers service"); + return rooms; } - async getCurrentRoom(username: string) + async getMessagesFromCurrentRoom(username: string): Promise { - console.log("-- in getCurrentRoom service"); - const user_db = await this.getUserByName(username); - //const user_db = await this.usersService.findOne(username); + console.log("-- in getMessagesFromCurrentRoom service"); - console.log("-- out getCurrentRoom service"); + const user_db = await this.getUserByName(username); + const currentRoom = await this.getRoomByName(user_db.currentRoom); + let messages = null; + if (currentRoom) + messages = currentRoom.messages; + + console.log("-- out getMessagesFromCurrentRoom service"); + return messages; + } + + async getCurrentRoomName(username: string): Promise + { + console.log("-- in getCurrentRoomName service"); + + const user_db = await this.getUserByName(username); + + console.log("-- out getCurrentRoomName service"); return user_db.currentRoom; } - async getRoomByName(name: string) + async getRoomByName(room_name: string): Promise { console.log("-- in getRoomByName service"); + const room = await this.chatroomRepository .createQueryBuilder('chatroom') - .where('chatroom.name = :name', { name: name }) + .where('chatroom.name = :name', { name: room_name }) .getOne(); + console.log("--- room:", room); console.log("-- out getRoomByName service"); return room; } - async getRoomById(id: number) + async getRoomById(id: number): Promise { console.log("-- in getRoomById service"); + const room = await this.chatroomRepository .createQueryBuilder('chatroom') .where('chatroom.id = :id', { id: id }) .getOne(); + console.log("--- room:", room); console.log("-- out getRoomById service"); return room; @@ -118,102 +172,180 @@ export class ChatService { /* SETTERS ************************************************ */ - async setCurrentRoom(user: User, name: string) + async setCurrentRoom(username: string, room_name: string): Promise { console.log("-- in setCurrentRoom service"); - const user_db = await this.getUserByName(user.username); - //const user_db = await this.usersService.findOne(user.username); - user_db.currentRoom = name; + + const user_db = await this.getUserByName(username); + user_db.currentRoom = room_name; this.userRepository.save(user_db); console.log("-- out setCurrentRoom service"); - return `room "${name}" is now current room`; + return `room "${room_name}" is now current room`; } /* ADDERS ************************************************* */ - async addUserToNewRoom(user: User, createRoomDto: createRoomDto) + async addUserToNewRoom(username: string, room: roomDto): Promise { - console.log("-- in addUserToRoom service"); - const room = await this.getRoomByName(createRoomDto.room_name); - if (room) - throw new HttpException(`This room already exist`, HttpStatus.CONFLICT); + console.log("-- in addUserToNewRoom service"); + + const find_room = await this.getRoomByName(room.name); + if (find_room) + throw new HttpException(`This room name already exist`, HttpStatus.CONFLICT); // create chatroom const newChatroom = new Chatroom(); - newChatroom.name = createRoomDto.room_name; - newChatroom.type = createRoomDto.room_type; - newChatroom.owner = user.username; - newChatroom.users = [user.username]; - newChatroom.messages = [{ name: "SERVER", message: `creation of room ${createRoomDto.room_name}` }]; - this.chatroomRepository.save(newChatroom); + newChatroom.name = room.name; + newChatroom.type = room.type; + newChatroom.owner = username; + newChatroom.users = [username]; + if (room.type === 'direct') + newChatroom.users = room.users; + newChatroom.messages = [{ name: "SERVER", message: `creation of room ${room.name}` }]; + await this.chatroomRepository.save(newChatroom); - console.log("-- out addUserToRoom service"); - return "successfull room creation"; + console.log("-- out addUserToNewRoom service"); } - async addUserToRoom(user: User, joinRoomDto: joinRoomDto) + async addUserToRoom(username: string, room_name: string): Promise { console.log("-- in addUserToRoom service"); - const room = await this.getRoomByName(joinRoomDto.room_name); - if (room.users.includes(user.username)) - throw new HttpException(`your have already join this room`, HttpStatus.CONFLICT); + + const room = await this.getRoomByName(room_name); + if (room.users.includes(username)) + throw new HttpException(`your have already joined this room`, HttpStatus.CONFLICT); // update room with new user - room.users.push(user.username); + room.users.push(username); this.chatroomRepository.save(room); - const rooms = await this.getMyRooms(user); - const allRooms = await this.getAllRooms(); - console.log("-- out addUserToRoom service"); - return "successfull joining room"; } - async addMessageToCurrentRoom(username: string, message: string) + async addMessageToRoom(room_name: string, username: string, message: string): Promise { - console.log("-- in addMessageToCurrentRoom service"); - const user_db = await this.getUserByName(username); - //const user_db = await this.usersService.findOne(username); - const currentRoom = await this.getRoomByName(user_db.currentRoom); + console.log("-- in addMessageToRoom service"); + + const my_room = await this.getRoomByName(room_name); let chat_message = { name: username, message: message, }; - currentRoom.messages.push(chat_message); - this.chatroomRepository.save(currentRoom); + my_room.messages.push(chat_message); + this.chatroomRepository.save(my_room); - console.log("-- out addMessageToCurrentRoom service"); + console.log("-- out addMessageToRoom service"); } /* REMOVERS *********************************************** */ - async removeUserFromRoom(user: User, room_name: string) + async removeUserFromRoom(username: string, room_name: string): Promise { console.log("-- in removeUserFromRoom service"); - // get room - // remove user + + const room = await this.getRoomByName(room_name); + if (!room.users.includes(username)) + throw new HttpException(`your are not in this room`, HttpStatus.CONFLICT); + + // delete user from room + room.users.push(username); + room.users = room.users.filter(name => name !== username); + this.chatroomRepository.save(room); + + // set current room to nothing + await this.setCurrentRoom(username, ""); + + console.log("-- out removeUserFromRoom service"); + return "successfully leaving room"; } /* SEARCH IN USER ***************************************** */ - async getUserByName(name: string) + async getUserByName(username: string): Promise { console.log("-- in getUserByName service"); + const user = await this.userRepository .createQueryBuilder('user') - .where('user.username = :name', { name: name }) + .where('user.username = :name', { name: username }) .getOne(); + console.log("--- user:", user); console.log("-- out getUserByName service"); return user; } + async getAllUsersNotMyRooms(username: string): Promise + { + console.log("-- in getAllUsersNotMyRooms service"); + + const directs = await this.getMyDirects(username); + + // get all users from directs + let usernames = directs.map(room => { + let user = room.users[0]; + if (user === username) + user = room.users[1]; + return user; + }); + usernames.push(username); + + const users = await this.userRepository + .createQueryBuilder('user') + .where('user.username NOT IN (:...usernames)', { usernames: usernames }) + .getMany(); + console.log("--- users:", users); + + console.log("-- out getAllUsersNotMyRooms service"); + return users; + } + + + /* GATEWAY EVENTS ***************************************** + */ + + async socketIncommingMessage(socket: socketDto, message: string): Promise + { + console.log("-- in handleSocketIncommingMessage service"); + + socket.to(socket.room).emit('message', socket.username, message); + let room_name = await this.getCurrentRoomName(socket.username); + await this.addMessageToRoom(room_name, socket.username, message); + + console.log("-- out handleSocketIncommingMessage service"); + } + + async socketChangeRoom(socket: socketDto, room_name: string): Promise + { + console.log('-- in socketChangeRoom service'); + + socket.leave(socket.room); + socket.join(room_name); + socket.room = room_name; + + console.log('-- out socketChangeRoom service'); + } + + async socketJoinRoom(socket: socketDto, room_name: string): Promise + { + console.log('- in socketJoinRoom service'); + + socket.leave(socket.room); + socket.join(room_name); + socket.room = room_name; + let message = `${socket.username} has join the room`; + await socket.to(socket.room).emit('message', "SERVER", message); + await this.addMessageToRoom(room_name, "SERVER", message); + + console.log('- out socketJoinRoom service'); + } + } diff --git a/srcs/requirements/nestjs/api_back/src/chat/dto/createRoom.dto.ts b/srcs/requirements/nestjs/api_back/src/chat/dto/createRoom.dto.ts deleted file mode 100644 index 69b5543e..00000000 --- a/srcs/requirements/nestjs/api_back/src/chat/dto/createRoom.dto.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional } from "class-validator"; -import { IsNull } from "typeorm"; - -export class createRoomDto -{ - @IsString() - @IsNotEmpty() - room_name: string; - - @IsString() - @IsNotEmpty() - room_type: 'public' | 'private' | 'direct'; - - @IsString() - @IsOptional() - room_password: string; -} - diff --git a/srcs/requirements/nestjs/api_back/src/chat/dto/messages.dto.ts b/srcs/requirements/nestjs/api_back/src/chat/dto/messages.dto.ts index 4933f230..53e25a2a 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/dto/messages.dto.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/dto/messages.dto.ts @@ -1,9 +1,13 @@ -import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional, IsArray } from "class-validator"; -import { IsNull } from "typeorm"; +import { IsString, IsOptional } from "class-validator"; export class messagesDto { - @IsArray() - messages: { name: string; message: string }[]; + @IsString() + @IsOptional() + name: string; + + @IsString() + @IsOptional() + message: string; } diff --git a/srcs/requirements/nestjs/api_back/src/chat/dto/room.dto.ts b/srcs/requirements/nestjs/api_back/src/chat/dto/room.dto.ts new file mode 100644 index 00000000..6dcf8099 --- /dev/null +++ b/srcs/requirements/nestjs/api_back/src/chat/dto/room.dto.ts @@ -0,0 +1,19 @@ +import { IsBoolean, IsEmpty, IsInt, IsIn, IsNotEmpty, IsNumber, IsArray, IsString, IsOptional, IsEnum } from "class-validator"; + +export class roomDto +{ + @IsString() + @IsNotEmpty() + name: string; + + @IsString() + @IsNotEmpty() + @IsIn(["public", "protected", "private", "direct", "user"]) + type: string; + + @IsArray() + @IsString({ each: true }) + @IsOptional() + users?: string[]; // usernames +} + diff --git a/srcs/requirements/nestjs/api_back/src/chat/dto/setCurrentRoom.dto.ts b/srcs/requirements/nestjs/api_back/src/chat/dto/setCurrentRoom.dto.ts index f5d0630a..41175023 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/dto/setCurrentRoom.dto.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/dto/setCurrentRoom.dto.ts @@ -1,5 +1,4 @@ import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional } from "class-validator"; -import { IsNull } from "typeorm"; export class setCurrentRoomDto { diff --git a/srcs/requirements/nestjs/api_back/src/chat/dto/joinRoom.dto.ts b/srcs/requirements/nestjs/api_back/src/chat/dto/socket.dto.ts similarity index 50% rename from srcs/requirements/nestjs/api_back/src/chat/dto/joinRoom.dto.ts rename to srcs/requirements/nestjs/api_back/src/chat/dto/socket.dto.ts index 8c81a93d..abc5350e 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/dto/joinRoom.dto.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/dto/socket.dto.ts @@ -1,10 +1,13 @@ import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString, IsOptional } from "class-validator"; -import { IsNull } from "typeorm"; +import { Socket } from 'socket.io'; -export class joinRoomDto +export class socketDto extends Socket { @IsString() - @IsNotEmpty() - room_name: string; + username: string; + + @IsString() + room: string; } + diff --git a/srcs/requirements/nestjs/api_back/src/chat/entities/chatroom.entity.ts b/srcs/requirements/nestjs/api_back/src/chat/entities/chatroom.entity.ts index 76810c91..89e8ff18 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/entities/chatroom.entity.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/entities/chatroom.entity.ts @@ -1,38 +1,36 @@ -import { - Entity, - Column, - ManyToOne, - ManyToMany, - JoinTable, - PrimaryGeneratedColumn -} from "typeorm"; +import { Entity, Column, ManyToOne, ManyToMany, JoinTable, PrimaryGeneratedColumn } from "typeorm"; +import { IsBoolean, IsEmpty, IsInt, IsIn, IsNotEmpty, IsNumber, IsArray, IsString, IsOptional, IsEnum } from "class-validator"; +import { Exclude, Expose } from 'class-transformer'; import { User } from 'src/users/entities/user.entity'; +import { messagesDto } from 'src/chat/dto/messages.dto'; @Entity('chatroom') -export class Chatroom { +export class Chatroom +{ @PrimaryGeneratedColumn() id: number; @Column() + @IsString() + @IsNotEmpty() name: string; - @Column() - type: string; - -// @ManyToOne(type => User, user => user.ownedRooms) -// owner: User; -// -// @ManyToMany(type => User) -// @JoinTable() -// users: User[]; + @Column() + @IsString() + @IsNotEmpty() + @IsIn(["public", "protected", "private", "direct", "user"]) + type: string; @Column() - owner: string; // name + owner: string; // username @Column("simple-array") - users: string[]; // names + @IsArray() + @IsString({ each: true }) + @IsOptional() + users?: string[]; // usernames @Column("json") - messages: { name: string, message: string }[]; + messages: messagesDto[]; } diff --git a/srcs/requirements/nestjs/api_back/src/game/game.service.ts b/srcs/requirements/nestjs/api_back/src/game/game.service.ts index 7f7f53e7..2e52357e 100644 --- a/srcs/requirements/nestjs/api_back/src/game/game.service.ts +++ b/srcs/requirements/nestjs/api_back/src/game/game.service.ts @@ -92,7 +92,6 @@ export class GameService { } this.userRepository.save(user); } - // if (grantTicketDto.isGameIsWithInvitation === true && user.status !== STATUS.IN_GAME) // WIP: need to fix STATUS.IN_GAME if (grantTicketDto.isGameIsWithInvitation === true) { const secondUser : Partial = await this.userService.findOne(grantTicketDto.playerTwoUsername) @@ -106,6 +105,7 @@ export class GameService { tok.token = encryptedTextToReturn; this.tokenGameRepository.save(tok); this.userService.updateStatus(user.id, STATUS.IN_POOL) + this.userService.updateStatus(secondUser.id, STATUS.IN_POOL) return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn }); } // else if (grantTicketDto.isGameIsWithInvitation === false && user.status !== STATUS.IN_GAME) { // WIP: need to fix STATUS.IN_GAME @@ -142,13 +142,11 @@ export class GameService { const userOne : User = await this.userRepository.createQueryBuilder('user') .where("user.username = :username", {username : tokenGame.playerOneUsername}) .getOne(); - this.userService.updateStatus(userOne.id, STATUS.IN_GAME) const userTwo : User = await this.userRepository.createQueryBuilder('user') .where("user.username = :username", {username : tokenGame.playerTwoUsername}) .getOne(); this.deleteToken(userOne) this.deleteToken(userTwo) - this.userService.updateStatus(userTwo.id, STATUS.IN_GAME) } return true; } @@ -168,7 +166,6 @@ export class GameService { const user : User = await this.userRepository.createQueryBuilder('user') .where("user.username = :username", {username : tokenGame.playerOneUsername}) .getOne(); - this.userService.updateStatus(user.id, STATUS.IN_GAME) this.deleteToken(user) return true; } @@ -259,6 +256,14 @@ export class GameService { this.gameRepository.save(game); if (!game) return HttpStatus.INTERNAL_SERVER_ERROR + const playerOne : User = await this.userRepository.createQueryBuilder('user') + .where("user.username = :username", {username : creategameDto.playerOneUsername}) + .getOne(); + const playerTwo : User = await this.userRepository.createQueryBuilder('user') + .where("user.username = :username", {username : creategameDto.playerTwoUsername}) + .getOne(); + this.userService.updateStatus(playerOne.id, STATUS.IN_GAME) + this.userService.updateStatus(playerTwo.id, STATUS.IN_GAME) console.log("200 retourné pour la création de partie") return HttpStatus.OK } @@ -280,7 +285,9 @@ export class GameService { const playerTwo = await this.userRepository.findOneBy({username : game.playerTwoUsername}) if (!playerOne || !playerTwo) return new HttpException("Internal Server Error. Impossible to update the database", HttpStatus.INTERNAL_SERVER_ERROR); - if (game.playerOneUsernameResult === game.playerTwoUsernameResult) + this.userService.updateStatus(playerOne.id, STATUS.CONNECTED) + this.userService.updateStatus(playerTwo.id, STATUS.CONNECTED) + if (game.playerOneUsernameResult === game.playerTwoUsernameResult) { this.userService.incrementDraws(playerOne.id) this.userService.incrementDraws(playerTwo.id) @@ -296,8 +303,6 @@ export class GameService { this.userService.incrementVictories(playerOne.id) this.userService.incrementDefeats(playerTwo.id) } - this.userService.updateStatus(playerOne.id, STATUS.CONNECTED) - this.userService.updateStatus(playerTwo.id, STATUS.CONNECTED) return HttpStatus.OK } diff --git a/srcs/requirements/nestjs/api_back/src/users/users.service.ts b/srcs/requirements/nestjs/api_back/src/users/users.service.ts index bd119c49..ff44e676 100644 --- a/srcs/requirements/nestjs/api_back/src/users/users.service.ts +++ b/srcs/requirements/nestjs/api_back/src/users/users.service.ts @@ -45,7 +45,6 @@ export class UsersService { isEnabledTwoFactorAuth: user.isEnabledTwoFactorAuth, status: user.status, stats: user.stats, - currentRoom: user.currentRoom, }; console.log(`Returned Partial User.` + partialUser.username + user.username); return partialUser; diff --git a/srcs/requirements/nginx/conf/.nfs000000000ef7127e000000f0 b/srcs/requirements/nginx/conf/.nfs000000000ef7127e000000f0 deleted file mode 100644 index 02ffec11..00000000 --- a/srcs/requirements/nginx/conf/.nfs000000000ef7127e000000f0 +++ /dev/null @@ -1,55 +0,0 @@ -server { - listen 8080; - listen [::]:8080; - server_name localhost; - - location /api/v2 { - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://backend_dev:3000; - } - - location /chat { - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_pass http://backend_dev:5000/chat; - } - - location /api/v2/game/gameserver { - deny all; - } - - location /pong { - proxy_pass http://game_server:8042/pong; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - proxy_set_header Host $host; - } - - location / { - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://frontend_dev:8080; - } -} - -server { - listen 35729 default_server; - listen [::]:35729 default_server; - server_name localhost; - location / { - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://frontend_dev:35729; - } -} diff --git a/srcs/requirements/svelte/api_front/src/pieces/chat/Interface_chat.ts b/srcs/requirements/svelte/api_front/src/pieces/chat/Interface_chat.ts new file mode 100644 index 00000000..ca9b761c --- /dev/null +++ b/srcs/requirements/svelte/api_front/src/pieces/chat/Interface_chat.ts @@ -0,0 +1,6 @@ +export interface Room +{ + name: string; + type: "public" | "protected" | "private" | "direct" | "user"; + users?: string[]; +} diff --git a/srcs/requirements/svelte/api_front/src/pieces/chat/Layout_create.svelte b/srcs/requirements/svelte/api_front/src/pieces/chat/Layout_create.svelte index 25ef6617..a68b70f6 100644 --- a/srcs/requirements/svelte/api_front/src/pieces/chat/Layout_create.svelte +++ b/srcs/requirements/svelte/api_front/src/pieces/chat/Layout_create.svelte @@ -1,12 +1,23 @@ @@ -57,7 +72,10 @@ {/if} - + +