seems like the merge worked

This commit is contained in:
Me
2023-01-05 12:30:54 +01:00
199 changed files with 30109 additions and 6796 deletions

View File

@@ -3,10 +3,8 @@ FROM node:alpine AS development
WORKDIR /usr/app
COPY ./api_back ./
COPY ./api_back/.env ./.env
COPY ./api_back/src/uploads/avatars/default.png ./uploads/avatars/default.png
RUN npm ci
RUN npm i
CMD [ "npm", "run", "start:dev" ]

File diff suppressed because it is too large Load Diff

View File

@@ -24,13 +24,13 @@
"@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.2.0",
"@nestjs/core": "^9.0.0",
"@nestjs/jwt": "^9.0.0",
"@nestjs/mapped-types": "^1.2.0",
"@nestjs/passport": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"@nestjs/platform-socket.io": "^9.2.1",
"@nestjs/typeorm": "^9.0.1",
"@nestjs/websockets": "^9.2.1",
"@types/express-session": "^1.17.5",
"@types/redis": "^4.0.11",
"@types/validator": "^13.7.9",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
@@ -41,7 +41,6 @@
"otplib": "^12.0.1",
"passport": "^0.6.0",
"passport-42": "^1.2.6",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
"pg": "^8.8.0",
"qrcode": "^1.5.1",
@@ -49,6 +48,7 @@
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"socket.io": "^4.5.4",
"typeorm": "^0.3.10",
"uuid": "^9.0.0"
},
@@ -59,8 +59,7 @@
"@types/express": "^4.17.13",
"@types/jest": "28.1.8",
"@types/multer": "^1.4.7",
"@types/node": "^16.0.0",
"@types/passport-jwt": "^3.0.7",
"@types/node": "^16.18.11",
"@types/passport-local": "^1.0.34",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",

View File

@@ -7,29 +7,35 @@ import { ConfigModule } from '@nestjs/config';
import { FriendshipsModule } from './friendship/friendships.module';
import { AuthenticationModule } from './auth/42/authentication.module';
import { PassportModule } from '@nestjs/passport';
// import { GameModule } from './game/game/game.module';
import { GameModule } from './game/game.module';
import { ChatGateway } from './chat/chat.gateway';
@Module({
imports: [UsersModule,
AuthenticationModule,
PassportModule.register({ session: true }),
FriendshipsModule,
ConfigModule.forRoot(),
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.POSTGRES_HOST,
port: parseInt(process.env.POSTGRES_PORT),
username: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DATABASE,
autoLoadEntities: true,
//ne pas synchroniser quand on est en prod. Trouver un moyen de set ça, sûrement
//avec une classe pour le module
synchronize: true,
}),
// GameModule,
],
imports: [
UsersModule,
AuthenticationModule,
PassportModule.register({ session: true }),
FriendshipsModule,
GameModule,
ConfigModule.forRoot(),
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.POSTGRES_HOST,
port: parseInt(process.env.POSTGRES_PORT),
username: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DATABASE,
autoLoadEntities: true,
//ne pas synchroniser quand on est en prod. Trouver un moyen de set ça, sûrement
//avec une classe pour le module
synchronize: true,
}),
// GameModule,
],
controllers: [AppController],
providers: [AppService],
providers: [
AppService,
ChatGateway,
],
})
export class AppModule {}

View File

@@ -37,10 +37,10 @@ export class AuthenticationController {
const user : User = request.user
if (user.isEnabledTwoFactorAuth === false || user.isTwoFactorAuthenticated === true){
console.log('ON VA VERS PROFILE');
return response.status(200).redirect('http://transcendance:8080/#/profile');
return response.status(200).redirect('http://' + process.env.WEBSITE_HOST + ':' + process.env.WEBSITE_PORT + '/#/profile');
}
console.log('ON VA VERS 2FA')
return response.status(200).redirect('http://transcendance:8080/#/2fa');
return response.status(200).redirect('http://' + process.env.WEBSITE_HOST + ':' + process.env.WEBSITE_PORT + '/#/2fa');
}
/**
@@ -83,6 +83,6 @@ export class AuthenticationController {
throw new UnauthorizedException('Wrong Code.');
await this.userService.authenticateUserWith2FA(request.user.id);
console.log('ON REDIRIGE');
return response.status(200).redirect('http://transcendance:8080/#/profile');
return response.status(200).redirect('http://' + process.env.WEBSITE_HOST + ':' + process.env.WEBSITE_PORT + '/#/profile');
}
}

View File

@@ -18,7 +18,7 @@ import { CreateUsersDto } from "src/users/dto/create-users.dto";
async validate(accessToken: string, refreshToken: string, profile: Profile, callbackURL: string) {
console.log("Validate inside strategy.ts");
console.log(profile.id, profile.username, profile.phoneNumbers[0].value, profile.emails[0].value, profile.photos[0].value);
const userDTO: CreateUsersDto = { fortyTwoId: profile.id, username: profile.username, email: profile.emails[0].value, image_url: 'default.png', isEnabledTwoFactorAuth: false , status: "connected" };
const userDTO: CreateUsersDto = { fortyTwoId: profile.id, username: profile.username, email: profile.emails[0].value, image_url: 'default.png', isEnabledTwoFactorAuth: false , status: "Connected" };
const user = await this.authenticationService.validateUser(userDTO);
if (!user)
throw new UnauthorizedException();

View File

@@ -0,0 +1,44 @@
import {
WebSocketGateway,
SubscribeMessage,
WebSocketServer,
MessageBody,
OnGatewayConnection,
OnGatewayDisconnect,
} from '@nestjs/websockets';
import { UsersService } from 'src/users/users.service';
import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto';
@WebSocketGateway(5000, {
path: '/chat',
})
export class ChatGateway
implements OnGatewayConnection, OnGatewayDisconnect
{
constructor
(
private usersService: UsersService,
) {}
@WebSocketServer()
server;
// how to guard the handleConnection ?
// https://github.com/nestjs/nest/issues/882
async handleConnection(client) {
// const paginationQuery = new PaginationQueryDto();
// const users = await this.usersService.findAll(paginationQuery);
// const users = await this.usersService.findAll(client);
// const users = await this.usersService.findAll(client);
console.log('---- Client connected :', client.id);
// console.log('users :', users);
}
handleDisconnect(client) {
console.log('---- client disconnected :', client.id);
}
/*
*/
}

View File

@@ -8,6 +8,12 @@ const MIME_TYPES = {
'image/png': 'png'
};
export enum STATUS {
CONNECTED = 'Connected',
DISCONNECTED = 'Disconnected',
IN_GAME = 'In Game',
IN_POOL = 'In Pool',
}
export const storageForAvatar = {
storage: diskStorage({
@@ -18,5 +24,13 @@ export const storageForAvatar = {
const extension : string = MIME_TYPES[file.mimetype];
cb(null, `${filename}${extension}`);
}
})
}),
fileFilter: (req, file, cb) => {
if (file.mimetype === 'image/png' || file.mimetype === 'image/jpg' || file.mimetype === 'image/jpeg') {
cb(null, true);
}
else {
cb(null, false);
}
}
}

View File

@@ -1,16 +0,0 @@
import { IsBoolean, IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class CreateUsersDto {
@IsString()
@IsNotEmpty()
readonly username: string;
readonly fortyTwoId: string;
@IsEmail()
readonly email: string;
@IsString()
readonly image_url: string;
@IsString()
readonly status: string;
@IsBoolean()
readonly isEnabledTwoFactorAuth: boolean;
}

View File

@@ -0,0 +1,19 @@
import { IsBoolean, IsNotEmpty, IsNumber, IsString } from "class-validator";
export class CreateGameDto {
@IsString()
@IsNotEmpty()
gameServerIdOfTheMatch : string
@IsNumber()
@IsNotEmpty()
gameOptions: number
@IsString()
@IsNotEmpty()
playerOneUsername : string
@IsString()
playerTwoUsername : string
@IsNumber()
playerTwoUsernameResult : number
@IsNumber()
playerOneUsernameResult : number
}

View File

@@ -0,0 +1,14 @@
import { IsBoolean, IsEmpty, IsInt, IsNotEmpty, IsNumber, IsString } from "class-validator";
import { IsNull } from "typeorm";
export class GrantTicketDto {
@IsString()
@IsNotEmpty()
playerOneUsername : string
@IsString()
playerTwoUsername : string
@IsNumber()
gameOptions : number
@IsBoolean()
isGameIsWithInvitation : boolean
}

View File

@@ -0,0 +1,5 @@
import { OmitType } from "@nestjs/mapped-types";
import { IsBoolean, IsNotEmpty, IsNumber, IsString } from "class-validator";
import { CreateGameDto } from "./createGame.dto";
export class UpdateGameDto extends OmitType(CreateGameDto, ['playerOneUsername', 'playerTwoUsername', 'gameOptions'] as const){}

View File

@@ -0,0 +1,16 @@
import { IsBase64, IsBoolean, IsEmpty, IsNotEmpty, IsNumber, IsString } from "class-validator";
export class ValidateTicketDto {
@IsString()
@IsNotEmpty()
playerOneUsername : string
@IsString()
playerTwoUsername : string
@IsNumber()
gameOptions : number
@IsBoolean()
isGameIsWithInvitation : boolean
@IsBase64()
@IsNotEmpty()
token : string
}

View File

@@ -0,0 +1,28 @@
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity('game')
export class Game {
@PrimaryGeneratedColumn()
id: number;
@Column()
playerOneUsername: string
@Column()
playerTwoUsername: string
@Column({default : 0, nullable : true})
playerOneUsernameResult : number
@Column({default : 0, nullable : true})
playerTwoUsernameResult : number
@Column({default : 0})
gameOptions: number
@Column({unique : true})
gameServerIdOfTheMatch: string
@Column({default: false, nullable : true}) //éric pourra trouver un meilleur mot : ongoing ?
isMatchIsFinished: boolean
}

View File

@@ -0,0 +1,22 @@
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity('tokenGame')
export class TokenGame {
@PrimaryGeneratedColumn()
id: number;
@Column()
playerOneUsername : string
@Column({nullable: true})
playerTwoUsername : string
@Column()
gameOptions : number
@Column()
isGameIsWithInvitation : boolean
@Column({default: 0, nullable: true})
numberOfRegisteredUser : number
@Column({default : false})
isSecondUserAcceptedRequest : boolean
@Column()
token : string
}

View File

@@ -1,22 +0,0 @@
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity('gameParty')
export class gameParty {
@PrimaryGeneratedColumn()
id: number;
@Column()
playerOne: string
@Column()
playerTwo: string
@Column()
resultOfTheMatch: string
@Column()
gameServerIdOfTheMatch: string
}

View File

@@ -1,4 +1,104 @@
import { Controller } from '@nestjs/common';
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 { 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 { GameService } from './game.service';
@Controller('game')
export class GameController {}
export class GameController {
constructor (private readonly gameService : GameService) { }
@Get('match/all')
@UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
async getMatchesForSpectator()
{
return this.gameService.getMatchesForSpectator();
}
@Get('ranking')
@UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
async getRankingForAllUsers(@Req() req)
{
const currentUser : User = req.user
return this.gameService.getRankingForAllUsers(currentUser);
}
@Post('ticket')
@UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
async grantTicket(@Req() req, @Body() grantTicketDto : GrantTicketDto, @Res() res : Response)
{
const user : User = req.user
if (grantTicketDto.playerOneUsername != user.username)
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, @Res() res : Response)
{
const user : User = req.user;
return this.gameService.declineInvitation(user, token, res);
}
@Post('accept')
@UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
async acceptInvitation(@Body('token') token, @Req() req, @Res() res : Response)
{
const user : User = req.user;
return this.gameService.acceptInvitation(user, token, res);
}
@Get('invitations')
@UseGuards(AuthenticateGuard)
@UseGuards(TwoFactorGuard)
async findInvitations(@Req() request, @Res() res : Response)
{
const user : User = request.user;
return this.gameService.findInvitations(user, res);
}
//
//N'est valable que pour le game-serveur.
@Post('gameserver/validate')
async validateTicket(@Body() validateTicketDto : ValidateTicketDto, @Req() request)
{
if (await this.gameService.validateToken(validateTicketDto) === false)
return new HttpException("The token is not valid", HttpStatus.NOT_FOUND);
console.log("200 retourné côté nest")
return HttpStatus.OK;
}
@Post('gameserver/creategame')
async createGame(@Body() creategameDto : CreateGameDto)
{
console.log("On est dans create game")
console.log(creategameDto)
return this.gameService.createGame(creategameDto);
}
@Post('gameserver/updategame')
async updateGame(@Body() updateGameDto : UpdateGameDto)
{
console.log("On est dans update game")
console.log(updateGameDto)
return this.gameService.updateGame(updateGameDto);
}
@Post('gameserver/destroysession')
async destroySession(@Body('token') token)
{
return this.gameService.destroySession(token);
}
}

View File

@@ -1,9 +1,17 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Friendship } from 'src/friendship/entities/friendship.entity';
import { FriendshipService } from 'src/friendship/friendship.service';
import { User } from 'src/users/entities/user.entity';
import { UsersService } from 'src/users/users.service';
import { Game } from './entity/game.entity';
import { TokenGame } from './entity/tokenGame.entity';
import { GameController } from './game.controller';
import { GameService } from './game.service';
@Module({
controllers: [GameController],
providers: [GameService]
imports: [TypeOrmModule.forFeature([TokenGame, User, Game, Friendship])],
controllers: [GameController],
providers: [GameService, UsersService, FriendshipService]
})
export class GameModule {}

View File

@@ -1,18 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { GameService } from './game.service';
// import { Test, TestingModule } from '@nestjs/testing';
// // import { GameService } from './game.service';
describe('GameService', () => {
let service: GameService;
// describe('GameService', () => {
// let service: GameService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [GameService],
}).compile();
// beforeEach(async () => {
// const module: TestingModule = await Test.createTestingModule({
// providers: [GameService],
// }).compile();
service = module.get<GameService>(GameService);
});
// service = module.get<GameService>(GameService);
// });
it('should be defined', () => {
expect(service).toBeDefined();
});
});
// it('should be defined', () => {
// expect(service).toBeDefined();
// });
// });

View File

@@ -1,4 +1,299 @@
import { 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';
import { TokenGame } from './entity/tokenGame.entity';
import { UsersService } from 'src/users/users.service';
import { CreateGameDto } from './dto/createGame.dto';
import { UpdateGameDto } from './dto/updateGame.dto';
import { FriendshipService } from 'src/friendship/friendship.service';
import { STATUS } from 'src/common/constants/constants';
@Injectable()
export class GameService {}
export class GameService {
constructor (
@InjectRepository(Game)
private readonly gameRepository : Repository<Game>,
@InjectRepository(User)
private readonly userRepository : Repository<User>,
@InjectRepository(TokenGame)
private readonly tokenGameRepository : Repository<TokenGame>,
private readonly userService : UsersService,
private readonly friendShipService : FriendshipService
) { }
async getMatchesForSpectator() {
const games = await this.gameRepository.createQueryBuilder("game")
.where('game.isMatchIsFinished = :isMatchIsFinished', {isMatchIsFinished : false})
.getMany();
const gamesToReturn : Partial<Game>[] = []
for (const game of games)
{
gamesToReturn.push({gameServerIdOfTheMatch : game.gameServerIdOfTheMatch,
gameOptions : game.gameOptions, playerOneUsername : game.playerOneUsername,
playerTwoUsername : game.playerTwoUsername})
console.log("Is match is finished : " + game.isMatchIsFinished)
}
return gamesToReturn;
}
async getRankingForAllUsers(currentUser : User) {
const users = await this.userRepository.createQueryBuilder("user")
.leftJoinAndSelect("user.stats", "stats")
.orderBy('stats.winGame', "DESC")
.getMany();
const partialUser : Partial<User>[] = []
for (const user of users)
{
if (await this.friendShipService.findIfUserIsBlockedOrHasBlocked(currentUser.id, user.id) === false)
partialUser.push({username : user.username, stats : user.stats })
}
console.log(...partialUser)
return partialUser;
}
async encryptToken(toEncrypt : string) : Promise<string> {
const iv = randomBytes(16);
const password = process.env.TICKET_FOR_PLAYING_GAME_SECRET + new Date();
const key = (await promisify(scrypt)(password, 'salt', 32)) as Buffer;
const cipher = createCipheriv('aes-256-ctr', key, iv);
const encryptedText = Buffer.concat([
cipher.update(toEncrypt),
cipher.final(),
]);
const encryptedTextToReturn = encryptedText.toString('base64');
return encryptedTextToReturn
}
async deleteToken(user : User){
const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokengame')
.where('tokengame.playerTwoUsername = :playerTwoUsername', {playerTwoUsername : user.username})
.orWhere('tokengame.playerOneUsername = :playerOneUsername', {playerOneUsername : user.username})
.getMany();
if (tokenGame)
return this.tokenGameRepository.remove(tokenGame);
}
async generateToken(user : User, grantTicketDto : GrantTicketDto, @Res() res : Response)
{
console.log(user.status);
if (user.status === STATUS.IN_POOL || user.status === STATUS.IN_GAME)
{
await this.deleteToken(user);
user.status = STATUS.CONNECTED;
this.userRepository.save(user);
}
if (grantTicketDto.isGameIsWithInvitation === true)
{
const secondUser : Partial<User> = await this.userService.findOne(grantTicketDto.playerTwoUsername)
if (!secondUser || secondUser.username === user.username)
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);
tok.isSecondUserAcceptedRequest = false;
tok.numberOfRegisteredUser = 0;
tok.token = encryptedTextToReturn;
this.tokenGameRepository.save(tok);
this.userService.updateStatus(user.id, "In Pool")
return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn });
}
else if (grantTicketDto.isGameIsWithInvitation === false) {
const encryptedTextToReturn = await this.encryptToken(user.username + '_'
+ grantTicketDto.gameOptions + '_' + grantTicketDto.isGameIsWithInvitation + '_' + new Date())
const tok = this.tokenGameRepository.create(grantTicketDto);
tok.numberOfRegisteredUser = 0;
tok.token = encryptedTextToReturn;
this.tokenGameRepository.save(tok);
this.userService.updateStatus(user.id, "In Pool")
return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn });
}
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({message : "Internal Server Error"});
}
async validateToken(validateTicketDto : ValidateTicketDto) {
if (validateTicketDto.isGameIsWithInvitation === true)
{
const tokenGame : TokenGame = await this.tokenGameRepository.createQueryBuilder('tokengame')
.where('tokengame.playerOneUsername = :playerOneUsername', {playerOneUsername : validateTicketDto.playerOneUsername})
.andWhere('tokengame.playerTwoUsername = :playerTwoUsername', {playerTwoUsername : validateTicketDto.playerTwoUsername})
.andWhere('tokengame.gameOptions = :gameOption', {gameOption : validateTicketDto.gameOptions})
.andWhere('tokengame.isGameIsWithInvitation = :isGameIsWithInvitation', {isGameIsWithInvitation: true})
.andWhere('tokengame.isSecondUserAcceptedRequest = :choice', {choice : true})
.andWhere('tokengame.token = :token', {token : validateTicketDto.token})
.getOne();
if (tokenGame)
{
tokenGame.numberOfRegisteredUser++;
if (tokenGame.numberOfRegisteredUser === 2)
{
this.tokenGameRepository.remove(tokenGame)
const userOne : User = await this.userRepository.createQueryBuilder('user')
.where("user.username = :username", {username : tokenGame.playerOneUsername})
.getOne();
this.userService.updateStatus(userOne.id, "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, "In Game")
}
return true;
}
}
else if (validateTicketDto.isGameIsWithInvitation === false)
{
const tokenGame : TokenGame = await this.tokenGameRepository.createQueryBuilder('tokengame')
.where('tokengame.playerOneUsername = :playerOneUsername', {playerOneUsername : validateTicketDto.playerOneUsername})
.andWhere('tokengame.gameOptions = :gameOption', {gameOption : validateTicketDto.gameOptions})
.andWhere('tokengame.isGameIsWithInvitation = :isGameIsWithInvitation', {isGameIsWithInvitation: false})
.andWhere('tokengame.token = :token', {token : validateTicketDto.token})
.getOne();
if (tokenGame)
{
this.tokenGameRepository.remove(tokenGame)
console.log("USERNAME : " + tokenGame.playerOneUsername)
const user : User = await this.userRepository.createQueryBuilder('user')
.where("user.username = :username", {username : tokenGame.playerOneUsername})
.getOne();
this.userService.updateStatus(user.id, "In Game")
this.deleteToken(user)
return true;
}
}
return false;
}
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 res.status(HttpStatus.NOT_FOUND).send({message : "No invitation found"});
let partialGame : Partial<TokenGame>[] = [];
for (const gameToken of game) {
partialGame.push({
playerOneUsername : gameToken.playerOneUsername,
playerTwoUsername : gameToken.playerTwoUsername,
gameOptions : gameToken.gameOptions,
token : gameToken.token,
});
}
return res.status(HttpStatus.OK).json(partialGame);
}
async declineInvitation(user : User, token : string, @Res() res : Response)
{
if (user.status !== "Connected")
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)
{
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)
{
console.log("On détruit le token et la session qui va avec")
const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokengame')
.where('tokengame.token = :token', {token : token})
.getOne();
if (tokenGame)
{
const playerOne = await this.userRepository.findOneBy({username : tokenGame.playerOneUsername})
const playerTwo = await this.userRepository.findOneBy({username : tokenGame.playerTwoUsername})
if (playerOne.status !== "Disconnected")
this.userService.updateStatus(playerOne.id, "Connected")
if (playerTwo.status !== "Disconnected")
this.userService.updateStatus(playerTwo.id, "Connected")
return this.tokenGameRepository.remove(tokenGame);
}
return new HttpException("Token not found !", HttpStatus.NOT_FOUND)
}
async acceptInvitation(user : User, token : string, @Res() res : Response)
{
if (user.status !== "Connected")
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})
.getOne();
if (tokenGame)
{
tokenGame.isSecondUserAcceptedRequest = true;
this.tokenGameRepository.save(tokenGame)
return res.status(HttpStatus.OK).json({message : "Invitation accepted."});
}
return res.status(HttpStatus.NOT_FOUND).json({message : "No invitation found !"});
}
async createGame(creategameDto : CreateGameDto)
{
if (creategameDto.playerOneUsername === "" || creategameDto.playerTwoUsername === ""
|| creategameDto.playerOneUsername === creategameDto.playerTwoUsername)
return HttpStatus.INTERNAL_SERVER_ERROR
const game = this.gameRepository.create(creategameDto)
game.isMatchIsFinished = false;
this.gameRepository.save(game);
if (!game)
return HttpStatus.INTERNAL_SERVER_ERROR
console.log("200 retourné pour la création de partie")
return HttpStatus.OK
}
async updateGame(updateGameDto : UpdateGameDto) {
console.log("Updata game" + updateGameDto)
const game = await this.gameRepository.createQueryBuilder('game')
.where("game.gameServerIdOfTheMatch = :gameServerIdOfTheMatch", {gameServerIdOfTheMatch : updateGameDto.gameServerIdOfTheMatch})
.getOne();
if (!game)
throw new HttpException(`The game could not be updated.`,HttpStatus.NOT_FOUND);
game.isMatchIsFinished = true;
game.playerOneUsernameResult = updateGameDto.playerOneUsernameResult
game.playerTwoUsernameResult = updateGameDto.playerTwoUsernameResult
this.gameRepository.save(game);
console.log("On a sauvegardé la partie. Game :")
console.log(game)
const playerOne = await this.userRepository.findOneBy({username : game.playerOneUsername})
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.incrementDraws(playerOne.id)
this.userService.incrementDraws(playerTwo.id)
}
else if (game.playerOneUsernameResult < game.playerTwoUsernameResult)
{
this.userService.incrementDefeats(playerOne.id)
this.userService.incrementVictories(playerTwo.id)
}
else
{
this.userService.incrementVictories(playerOne.id)
this.userService.incrementDefeats(playerTwo.id)
}
this.userService.updateStatus(playerOne.id, "Connected")
this.userService.updateStatus(playerTwo.id, "Connected")
return HttpStatus.OK
}
}

View File

@@ -8,7 +8,7 @@ import * as connectRedis from 'connect-redis';
async function bootstrap() {
const app = await NestFactory.create(AppModule, { cors: true });
const port = process.env.PORT || 3001;
const port = process.env.PORT || 3000;
const client = redis.createClient(
{
socket: { host: process.env.REDIS_HOST, port: parseInt(process.env.REDIS_PORT) },
@@ -50,6 +50,6 @@ async function bootstrap() {
);
app.use(passport.initialize());
app.use(passport.session());
await app.listen(port, () => { console.log(`Listening on port ${port}`); });
await app.listen(port, () => { console.log(`Listening on port ${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}}`); });
}
bootstrap();

View File

@@ -2,7 +2,7 @@
// et de les mettre comme optionnelles. De plus on peut hériter
// des décorateurs de la classe parente (par exemple @IsString()).
import { OmitType, PartialType } from "@nestjs/mapped-types";
import { OmitType } from "@nestjs/mapped-types";
import { CreateUsersDto } from "./create-users.dto";
export class UpdateUsersDto extends OmitType(CreateUsersDto, ['fortyTwoId', 'email', 'image_url', 'status'] as const){}

View File

@@ -28,7 +28,7 @@ export class User {
@Column({ nullable: true })
phone: string;
@Column({ default: 'disconnected' })
@Column({ default: 'Disconnected' })
status: string;
// @Column()

View File

@@ -1,5 +1,5 @@
import {
Body, Controller, Delete, Get, NotFoundException, Param, Patch, Post, Query, Redirect, Req, Res, UploadedFile, UseGuards, UseInterceptors
Body, Controller, Delete, Get, NotFoundException,HttpStatus, Param, Patch, Post, Query, Redirect, Req, Res, UploadedFile, UseGuards, UseInterceptors
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { Response } from 'express';
@@ -84,12 +84,9 @@ export class UsersController {
@UseGuards(TwoFactorGuard)
@Patch()
async update(@Req() req, @Body(new ValidationPipe()) usersUpdateDto: UpdateUsersDto, @Res() response : Response) {
console.log("DANS PATCH USERS");
const user = await this.usersService.update(req.user.id, usersUpdateDto);
// const user : User = req.user;
if (user.isEnabledTwoFactorAuth === false && user.isTwoFactorAuthenticated === true)
this.usersService.setIsTwoFactorAuthenticatedWhenLogout(user.id);
console.log ("Enbale 2FA " + user.isEnabledTwoFactorAuth + " Is authenticated " + user.isTwoFactorAuthenticated);
if (user.isEnabledTwoFactorAuth === true && user.isTwoFactorAuthenticated === false)
{
response.status(201).send('2FA redirect')
@@ -111,9 +108,14 @@ export class UsersController {
@UseGuards(TwoFactorGuard)
@Post('avatar')
@UseInterceptors(FileInterceptor('file', storageForAvatar))
uploadAvatar(@UploadedFile() file, @Req() request){
uploadAvatar(@UploadedFile() file, @Req() request, @Res() res){
const user : User = request.user;
this.usersService.updateAvatar(user.id, file.filename);
if (file)
{
this.usersService.updateAvatar(user.id, file.filename);
return res.status(HttpStatus.OK).json({message : "Avatar updated"});
}
return res.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE).json({message : "Unsupported media type. Please use a valid image file."});
}
// GET http://transcendance:8080/user/avatar

View File

@@ -4,12 +4,9 @@ import { User } from './entities/user.entity';
import { Repository, Not } from 'typeorm';
import { CreateUsersDto } from './dto/create-users.dto';
import { UpdateUsersDto } from './dto/update-users.dto';
import { Friendship } from '../friendship/entities/friendship.entity';
import { PaginationQueryDto } from 'src/common/dto/pagination-query.dto';
import { UserStats } from './entities/userStat.entities';
import { FriendshipService } from 'src/friendship/friendship.service';
import { stringify } from 'querystring';
// On va devoir sûrement trouver un moyen plus simple pour passer l'id, sûrement via des pipes
// ou des interceptors, mais pour l'instant on va faire comme ça.
@Injectable()
@@ -86,6 +83,28 @@ export class UsersService {
return partialUsers;
}
// async findAbsolutelyAll() {
// // const otherUsers = await this.userRepository.find({where: {id: Not(+currentUser.id)}, order: {username: "ASC"}, skip: offset, take: limit,});
// const otherUsers = await this.userRepository.find({order: {username: "ASC"}});
// let partialUsers : Partial<User>[] = [];
// for (const otherUser of otherUsers) {
// console.log('other user: ')
// console.log({...otherUser})
// let tmp = await this.friendshipService.findIfUserIsBlockedOrHasBlocked(currentUser.id, otherUser.id);
// console.log('user.services findIF Blocked... : ')
// console.log(tmp)
// if (tmp === false) {
// // if (await this.friendshipService.findIfUserIsBlockedOrHasBlocked(currentUser.id, otherUser.id) === false) {
// partialUsers.push({username: otherUser.username, image_url: otherUser.image_url, status: otherUser.status, stats: otherUser.stats});
// }
// }
// console.log('user.services findAll, partialUsers:')
// console.log({...partialUsers})
// return partialUsers;
// }
async create(createUserDto: CreateUsersDto) {