diff --git a/Makefile b/Makefile index fed7dc65..978b61b8 100644 --- a/Makefile +++ b/Makefile @@ -15,11 +15,11 @@ prod: start: docker compose -f ${DOCKERCOMPOSEPATH} start - docker logs --follow srcs-backend_dev-1 + docker logs --follow nestjs start_dev: docker compose -f ${DOCKERCOMPOSEPATH} start dev - docker logs --follow srcs-backend_dev-1 + docker logs --follow nestjs start_prod: docker compose -f ${DOCKERCOMPOSEPATH} start prod @@ -30,10 +30,6 @@ down: docker compose -f ${DOCKERCOMPOSEPATH} -v down destroy: - # rm -rf ./srcs/requirements/nestjs/api_back/node_modules/ - # rm -rf ./srcs/requirements/nestjs/api_back/dist - # rm -rf ./srcs/requirements/svelte/api_front/node_modules/ - # rm -rf ./srcs/requirements/svelte/api_front/public/build docker compose -f ${DOCKERCOMPOSEPATH} down -v --rmi all --remove-orphans docker ps -aq | xargs --no-run-if-empty docker rm -f docker images -aq | xargs --no-run-if-empty docker rmi -f diff --git a/README.md b/README.md index ffefe99a..ff078f92 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,14 @@ - [x] Utilisateur : faire la base pour un utilisateur - [x] Utilisateur : faire le système de requêtes amis -- [ ] Utilisateur : mettre en place le système de session (voire de statut ?) -- [ ] Utilisateur : mettre en place le système d'avatar -- [ ] Utilisateur : mettre en place la double authentification -- [ ] Utilisateur : mettre en place le système d'Oauth -- [ ] Utilisateur : mettre en place la hashage de mot de passe (avec Oauth) -- [ ] Utilisateur : mettre en place le système de statut -- [ ] Utilisateur : mettre en place le système de stats -- [ ] Utilisateur : mettre en place l'historique des matches +- [x] Utilisateur : mettre en place le système de session (voire de statut ?) +- [x] Utilisateur : mettre en place le système d'avatar +- [x] Utilisateur : mettre en place la double authentification +- [x] Utilisateur : mettre en place le système d'Oauth +- [x] Utilisateur : mettre en place la hashage de mot de passe (avec Oauth) +- [x] Utilisateur : mettre en place le système de statut +- [x] Utilisateur : mettre en place le système de stats +- [x] Utilisateur : mettre en place l'historique des matches ### TODO List : Docker édition. diff --git a/make_env.sh b/make_env.sh new file mode 100644 index 00000000..5f28ea5e --- /dev/null +++ b/make_env.sh @@ -0,0 +1,6 @@ +#! /usr/bin/env bash + +# This script is used to create a new environment for the project. + +# Create a new environment for docker + diff --git a/memo.txt b/memo.txt deleted file mode 100644 index 48f096cc..00000000 --- a/memo.txt +++ /dev/null @@ -1,50 +0,0 @@ -DONE : - - -TODO : - - timeout in gameserver for private match (TO test) - - - If in game, destroy game scripts stuff when changing page. - (I need to dig deeper in svelte to know how this could work) - - mode spectateur - - quelques routes cote serveur - - une interface cote front (liste des matchs en cours) - - etat du client (en ligne, en jeu, ...) - - le chat - --_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_- -"Bonus" : - - HTTPS - - mettre le site en ligne --_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_- -BUG : - - Bug de son étonnant dans le front, ça pop une fois de temps en temps : - Uncaught (in promise) DOMException: The element has no supported sources. - 18.ogg et 24.ogg bug peut-etre. - - l'avatar ne se charge pas après avoir redémarré les containers (mais sans avoir supprimé les volumes) - normal ou oubli ? --_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_- - -- Comment fonctionne .env ? Comment faire pour ne pas le push sur le depot ? - -- certains status 200 pourrait peut-être être du 204 ? - (exemple dans TwoFactorAuthentication.svelte) - -A la place de : -``` - if (response.status === 401) { - // Wrong - } - if (response.status === 200) { - // Ok - } -``` -On pourrait mettre : -``` - if (!response.ok) { - // Wrong - } - else { - // Ok - } -``` diff --git a/srcs/docker-compose.yml b/srcs/docker-compose.yml index 56d1199d..6eb0b483 100644 --- a/srcs/docker-compose.yml +++ b/srcs/docker-compose.yml @@ -1,5 +1,6 @@ services: backend_dev: + container_name: nestjs build: context: ./requirements/nestjs target: development @@ -7,6 +8,7 @@ services: volumes: - ./requirements/nestjs/api_back/src:/usr/app/src - ./requirements/nestjs/api_back/test:/usr/app/test/ + - nestjs_photos_volume:/usr/app/src/uploads/avatars env_file: - .env environment: @@ -17,6 +19,7 @@ services: - redis game_server: + container_name: game_server build: context: ./requirements/game_server dockerfile: Dockerfile @@ -29,6 +32,7 @@ services: - backend_dev frontend_dev: + container_name: svelte build: context: ./requirements/svelte target: development @@ -50,6 +54,7 @@ services: # t'embete pas a gerer ton propre container nginx nginx: + container_name: nginx image: nginx:alpine restart: unless-stopped volumes: @@ -65,7 +70,7 @@ services: - redis postgresql: - container_name: nestjs_postgresql + container_name: postgresql image: postgres volumes: - data_nest_postgresql:/var/lib/postgresql/data @@ -79,7 +84,7 @@ services: # Je connais pas redis, mais si t'en a besoin que a l'interieur de tes containers, je pense pas que t'as besoin d'un expose. redis: - container_name: nestjs_redis + container_name: redis image: redis:alpine restart: unless-stopped environment: @@ -88,3 +93,4 @@ services: volumes: data_nest_postgresql: + nestjs_photos_volume: diff --git a/srcs/requirements/nestjs/api_back/src/game/game.controller.ts b/srcs/requirements/nestjs/api_back/src/game/game.controller.ts index 09d1a650..b6b3770b 100644 --- a/srcs/requirements/nestjs/api_back/src/game/game.controller.ts +++ b/srcs/requirements/nestjs/api_back/src/game/game.controller.ts @@ -1,15 +1,11 @@ -import { Body, Controller, Get, HttpException, HttpStatus, Post, Req, UseGuards } from '@nestjs/common'; -import { Console } from 'console'; -import { request } from 'http'; -import { use } from 'passport'; +import { Body, Controller, Get, HttpException, HttpStatus, Post, Req, Res, UseGuards } from '@nestjs/common'; import { AuthenticateGuard, TwoFactorGuard } from 'src/auth/42/guards/42guards'; import { User } from 'src/users/entities/user.entity'; -import { UsersService } from 'src/users/users.service'; +import { Response } from 'express'; import { CreateGameDto } from './dto/createGame.dto'; import { GrantTicketDto } from './dto/grantTicket.dto'; import { UpdateGameDto } from './dto/updateGame.dto'; import { ValidateTicketDto } from './dto/validateTicket.dto'; -import { TokenGame } from './entity/tokenGame.entity'; import { GameService } from './game.service'; @Controller('game') @@ -29,51 +25,40 @@ export class GameController { @Post('ticket') @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) - async grantTicket(@Req() req, @Body() grantTicketDto : GrantTicketDto) + async grantTicket(@Req() req, @Body() grantTicketDto : GrantTicketDto, @Res() res : Response) { const user : User = req.user if (grantTicketDto.playerOneUsername != user.username) - return new HttpException('You can\'t request a game for another person.', 403 ) - // else if (user.status !== "connected") - // return new HttpException('You must not be in game...', HttpStatus.FORBIDDEN ) - return this.gameService.generateToken(user, grantTicketDto); - } - - @Post('requested') - @UseGuards(AuthenticateGuard) - @UseGuards(TwoFactorGuard) - async requestIfAnotherUserHasRespondToquestForGame(@Req() req, @Body('token') token) - { - const user : User = req.user; - return this.gameService.requestIfAnotherUserHasRespondToquestForGame(user, token); + return res.status(HttpStatus.BAD_REQUEST).json({message : 'You can\'t grant a ticket to another user'}); + return this.gameService.generateToken(user, grantTicketDto, res); } @Post('decline') @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) - async declineInvitation(@Body('token') token, @Req() req) + async declineInvitation(@Body('token') token, @Req() req, @Res() res : Response) { const user : User = req.user; - return this.gameService.declineInvitation(user, token); + return this.gameService.declineInvitation(user, token, res); } @Post('accept') @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) - async acceptInvitation(@Body('token') token, @Req() req) + async acceptInvitation(@Body('token') token, @Req() req, @Res() res : Response) { const user : User = req.user; - return this.gameService.acceptInvitation(user, token); + return this.gameService.acceptInvitation(user, token, res); } @Get('invitations') @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) - async findInvitations(@Req() request) + async findInvitations(@Req() request, @Res() res : Response) { const user : User = request.user; - return this.gameService.findInvitations(user); + return this.gameService.findInvitations(user, res); } // @@ -95,9 +80,6 @@ export class GameController { return this.gameService.createGame(creategameDto); } - - - @Post('gameserver/updategame') async updateGame(@Body() updateGameDto : UpdateGameDto) { @@ -111,6 +93,4 @@ export class GameController { { return this.gameService.destroySession(token); } - - } 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 9d7c28d9..f00c941d 100644 --- a/srcs/requirements/nestjs/api_back/src/game/game.service.ts +++ b/srcs/requirements/nestjs/api_back/src/game/game.service.ts @@ -1,9 +1,10 @@ -import { ConsoleLogger, HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable, Res } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { createCipheriv, randomBytes, scrypt } from 'crypto'; import { User } from 'src/users/entities/user.entity'; import { Repository } from 'typeorm'; import { promisify } from 'util'; +import { Response } from 'express'; import { GrantTicketDto } from './dto/grantTicket.dto'; import { Game } from './entity/game.entity'; import { ValidateTicketDto } from './dto/validateTicket.dto'; @@ -65,8 +66,7 @@ export class GameService { return this.tokenGameRepository.remove(tokenGame); } - - async generateToken(user : User, grantTicketDto : GrantTicketDto) + async generateToken(user : User, grantTicketDto : GrantTicketDto, @Res() res : Response) { console.log(user.status); if (user.status === STATUS.IN_POOL || user.status === STATUS.IN_GAME) @@ -79,7 +79,7 @@ export class GameService { { const secondUser : Partial = await this.userService.findOneByUsername(user.id.toString(), grantTicketDto.playerTwoUsername) if (!secondUser || secondUser.username === user.username) - return new HttpException("The requested second player does not exist OR you want to play against yourself. :P", HttpStatus.NOT_FOUND); + return res.status(HttpStatus.NOT_FOUND).json({message : "User not found OR you want to play with yourself."}); const encryptedTextToReturn = await this.encryptToken(user.username + '_' + secondUser.username + '_' + grantTicketDto.gameOptions + '_' + grantTicketDto.isGameIsWithInvitation + '_' + new Date()) const tok = this.tokenGameRepository.create(grantTicketDto); @@ -88,7 +88,7 @@ export class GameService { tok.token = encryptedTextToReturn; this.tokenGameRepository.save(tok); this.userService.updateStatus(user.id, "In Pool") - return { token : encryptedTextToReturn }; + return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn }); } else if (grantTicketDto.isGameIsWithInvitation === false) { const encryptedTextToReturn = await this.encryptToken(user.username + '_' @@ -98,9 +98,9 @@ export class GameService { tok.token = encryptedTextToReturn; this.tokenGameRepository.save(tok); this.userService.updateStatus(user.id, "In Pool") - return { token : encryptedTextToReturn }; + return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn }); } - return new HttpException("Something went wrong !", HttpStatus.INTERNAL_SERVER_ERROR) + return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({message : "Internal Server Error"}); } async validateToken(validateTicketDto : ValidateTicketDto) { @@ -157,14 +157,14 @@ export class GameService { return false; } - async findInvitations(user : User) { + async findInvitations(user : User, @Res() res : Response) { const game = await this.tokenGameRepository.createQueryBuilder('tokengame') .where('tokengame.playerTwoUsername = :playerTwoUsername', {playerTwoUsername : user.username}) .andWhere('tokengame.isGameIsWithInvitation = :invit', {invit : true}) .andWhere('tokengame.isSecondUserAcceptedRequest = :choice', {choice : false}) .getMany(); if (!game) - return new HttpException( "No invitations !", HttpStatus.NOT_FOUND); + return res.status(HttpStatus.NOT_FOUND).send({message : "No invitation found"}); let partialGame : Partial[] = []; for (const gameToken of game) { partialGame.push({ @@ -174,21 +174,24 @@ export class GameService { token : gameToken.token, }); } - return partialGame; + return res.status(HttpStatus.OK).json(partialGame); } - async declineInvitation(user : User, token : string) + async declineInvitation(user : User, token : string, @Res() res : Response) { if (user.status !== "Connected") - return new HttpException("You must finish your game before decline.", HttpStatus.FORBIDDEN) + return res.status(HttpStatus.FORBIDDEN).json({message : "You must not be in game to decline an invitation"}); console.log("On décline l'invitation") const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokengame') .andWhere('tokengame.playerTwoUsername = :playerTwoUsername', {playerTwoUsername : user.username}) .andWhere('tokengame.token = :token', {token : token}) .getOne(); if (tokenGame) - return this.tokenGameRepository.remove(tokenGame); - return new HttpException("Invitation not found !", HttpStatus.NOT_FOUND) + { + this.tokenGameRepository.remove(tokenGame); + return res.status(HttpStatus.OK).json({message : "Invitation declined."}); + } + return res.status(HttpStatus.NOT_FOUND).json({message : "No invitation found !"}); } async destroySession(token : string) @@ -210,10 +213,10 @@ export class GameService { return new HttpException("Token not found !", HttpStatus.NOT_FOUND) } - async acceptInvitation(user : User, token : string) + async acceptInvitation(user : User, token : string, @Res() res : Response) { if (user.status !== "Connected") - return new HttpException("You must finish your game before accept.", HttpStatus.FORBIDDEN) + return res.status(HttpStatus.FORBIDDEN).send("") const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokenGame') .andWhere('tokenGame.playerTwoUsername = :playerTwoUsername', {playerTwoUsername : user.username}) .andWhere('tokenGame.token = :token', {token : token}) @@ -222,27 +225,11 @@ export class GameService { { tokenGame.isSecondUserAcceptedRequest = true; this.tokenGameRepository.save(tokenGame) - return HttpStatus.OK + return res.status(HttpStatus.OK).json({message : "Invitation accepted."}); } - return new HttpException("Invitation not found !", HttpStatus.NOT_FOUND) + return res.status(HttpStatus.NOT_FOUND).json({message : "No invitation found !"}); } - async requestIfAnotherUserHasRespondToquestForGame(user : User, token : string) { - if (user.status !== "Connected") - return new HttpException("You can't do that.", HttpStatus.BAD_REQUEST) - const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokenGame') - .where('tokenGame.token = :token', {token : token}) - .andWhere('tokenGame.isSecondUserAcceptedRequest = :isSecondUserAcceptedRequest', {isSecondUserAcceptedRequest : true}) - .getOne(); - if (tokenGame && tokenGame.isSecondUserAcceptedRequest === true) - return {isSecondUserAcceptedRequest : true} - else if (tokenGame && tokenGame.isSecondUserAcceptedRequest === false) - return {isSecondUserAcceptedRequest : false} - else if (!tokenGame) - return new HttpException("Not Found", HttpStatus.NOT_FOUND) - } - - async createGame(creategameDto : CreateGameDto) { if (creategameDto.playerOneUsername === "" || creategameDto.playerTwoUsername === "" diff --git a/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte b/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte index 88b6d176..d9b37fe3 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte +++ b/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte @@ -82,7 +82,8 @@ const responseInjson = await responseFromServer.json(); const token : string = responseInjson.token; showWaitPage = false; - if (!responseFromServer.ok || (responseFromServer.status != 200 && responseFromServer.status != 201)) + console.log("status : " + responseFromServer.status) + if (responseFromServer.status != 200) { console.log(responseInjson) console.log("On refuse le ticket"); @@ -137,10 +138,12 @@ clearInterval(idOfIntevalCheckTerminationOfTheMatch); console.log("matchTermitation was called") showWaitPage = false - matchAbort ? errorMessageWhenAttemptingToGetATicket = "The match has been aborted" : errorMessageWhenAttemptingToGetATicket = "The match is finished !" + matchAbort ? + errorMessageWhenAttemptingToGetATicket = "The match has been aborted" + : errorMessageWhenAttemptingToGetATicket = "The match is finished !" matchAbort ? showError = true : showMatchEnded = true; - hiddenGame = true; setTimeout(() => { + hiddenGame = true; showError = false; showMatchEnded = false; optionsAreNotSet = true @@ -216,25 +219,22 @@ Might become useless after CSS rework. -->
- - - {#if showError === true} -
-
- Error -

{errorMessageWhenAttemptingToGetATicket}

- -
-
- {/if} - {#if showMatchEnded === true}

{errorMessageWhenAttemptingToGetATicket}

{/if} + {#if showError === true} +
+
+ Error +

{errorMessageWhenAttemptingToGetATicket}

+
+
+ {/if} + {#if showWaitPage === true}