diff --git a/.env_sample b/.env_sample deleted file mode 100644 index 5ffbd46..0000000 --- a/.env_sample +++ /dev/null @@ -1,25 +0,0 @@ -POSTGRES_HOST=postgres -POSTGRES_PORT=5432 -POSTGRES_USER=postgres_usr -POSTGRES_PASSWORD=postgres_pw -POSTGRES_DB=transcendence - -PGADMIN_DEFAULT_EMAIL=admin@pg.com -PGADMIN_DEFAULT_PASSWORD=admin - -MAIL_USER=vaganiwast@gmail.com -MAIL_PASSWORD= - -FRONT_FPS=144 - -HOST=localhost -FRONT_PORT=80 -BACK_PORT=3001 -HASH_SALT=10 - -JWT_SECRET=test -JWT_EXPIRATION_TIME=900 - -FT_OAUTH_CLIENT_ID= -FT_OAUTH_CLIENT_SECRET= -FT_OAUTH_CALLBACK_URL="http://$HOST:$BACK_PORT/log/inReturn" diff --git a/back/volume/.eslintrc.js b/back/volume/.eslintrc.js index 68e0778..651eeec 100644 --- a/back/volume/.eslintrc.js +++ b/back/volume/.eslintrc.js @@ -1,13 +1,13 @@ module.exports = { env: { browser: true, - es2021: true + es2021: true, }, - extends: 'standard-with-typescript', + extends: "standard-with-typescript", parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - project: ['./tsconfig.json'], - tsconfigRootDir: __dirname - } -} + ecmaVersion: "latest", + sourceType: "module", + project: ["./tsconfig.json"], + tsconfigRootDir: __dirname, + }, +}; diff --git a/back/volume/src/app.module.ts b/back/volume/src/app.module.ts index 2e363df..7d00622 100644 --- a/back/volume/src/app.module.ts +++ b/back/volume/src/app.module.ts @@ -1,13 +1,13 @@ -import { Module } from '@nestjs/common' -import { ConfigModule, ConfigService } from '@nestjs/config' -import { TypeOrmModule } from '@nestjs/typeorm' -import * as Joi from 'joi' -import { ScheduleModule } from '@nestjs/schedule' +import { Module } from "@nestjs/common"; +import { ConfigModule, ConfigService } from "@nestjs/config"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import * as Joi from "joi"; +import { ScheduleModule } from "@nestjs/schedule"; -import { AuthModule } from './auth/auth.module' -import { ChatModule } from './chat/chat.module' -import { PongModule } from './pong/pong.module' -import { UsersModule } from './users/users.module' +import { AuthModule } from "./auth/auth.module"; +import { ChatModule } from "./chat/chat.module"; +import { PongModule } from "./pong/pong.module"; +import { UsersModule } from "./users/users.module"; @Module({ imports: [ @@ -26,28 +26,28 @@ import { UsersModule } from './users/users.module' HOST: Joi.string().required(), FRONT_PORT: Joi.number().required(), BACK_PORT: Joi.number().required(), - HASH_SALT: Joi.number().required() - }) + HASH_SALT: Joi.number().required(), + }), }), TypeOrmModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (configService: ConfigService) => ({ - type: 'postgres', - host: configService.get('POSTGRES_HOST'), - port: configService.get('POSTGRES_PORT'), - username: configService.get('POSTGRES_USER'), - password: configService.get('POSTGRES_PASSWORD'), - database: configService.get('POSTGRES_DB'), - jwt_secret: configService.get('JWT_SECRET'), + type: "postgres", + host: configService.get("POSTGRES_HOST"), + port: configService.get("POSTGRES_PORT"), + username: configService.get("POSTGRES_USER"), + password: configService.get("POSTGRES_PASSWORD"), + database: configService.get("POSTGRES_DB"), + jwt_secret: configService.get("JWT_SECRET"), autoLoadEntities: true, - synchronize: true - }) + synchronize: true, + }), }), AuthModule, ChatModule, PongModule, - UsersModule - ] + UsersModule, + ], }) export class AppModule {} diff --git a/back/volume/src/auth/42-auth.guard.ts b/back/volume/src/auth/42-auth.guard.ts index 3f5f530..1187b3b 100644 --- a/back/volume/src/auth/42-auth.guard.ts +++ b/back/volume/src/auth/42-auth.guard.ts @@ -1,25 +1,25 @@ import { type ExecutionContext, Injectable, - type CanActivate -} from '@nestjs/common' -import { AuthGuard } from '@nestjs/passport' -import { type Request } from 'express' + type CanActivate, +} from "@nestjs/common"; +import { AuthGuard } from "@nestjs/passport"; +import { type Request } from "express"; @Injectable() -export class FtOauthGuard extends AuthGuard('42') { - async canActivate (context: ExecutionContext): Promise { - const activate: boolean = (await super.canActivate(context)) as boolean - const request: Request = context.switchToHttp().getRequest() - await super.logIn(request) - return activate +export class FtOauthGuard extends AuthGuard("42") { + async canActivate(context: ExecutionContext): Promise { + const activate: boolean = (await super.canActivate(context)) as boolean; + const request: Request = context.switchToHttp().getRequest(); + await super.logIn(request); + return activate; } } @Injectable() export class AuthenticatedGuard implements CanActivate { - async canActivate (context: ExecutionContext): Promise { - const req: Request = context.switchToHttp().getRequest() - return req.isAuthenticated() + async canActivate(context: ExecutionContext): Promise { + const req: Request = context.switchToHttp().getRequest(); + return req.isAuthenticated(); } } diff --git a/back/volume/src/auth/42.decorator.ts b/back/volume/src/auth/42.decorator.ts index 912cee6..639c85f 100644 --- a/back/volume/src/auth/42.decorator.ts +++ b/back/volume/src/auth/42.decorator.ts @@ -1,9 +1,9 @@ -import { createParamDecorator, type ExecutionContext } from '@nestjs/common' -import { type Profile } from 'passport-42' +import { createParamDecorator, type ExecutionContext } from "@nestjs/common"; +import { type Profile } from "passport-42"; export const Profile42 = createParamDecorator( (data: unknown, ctx: ExecutionContext): Profile => { - const request = ctx.switchToHttp().getRequest() - return request.user + const request = ctx.switchToHttp().getRequest(); + return request.user; } -) +); diff --git a/back/volume/src/auth/42.strategy.ts b/back/volume/src/auth/42.strategy.ts index 7842580..461096c 100644 --- a/back/volume/src/auth/42.strategy.ts +++ b/back/volume/src/auth/42.strategy.ts @@ -1,49 +1,49 @@ -import { Injectable } from '@nestjs/common' -import { ConfigService } from '@nestjs/config' -import { PassportStrategy } from '@nestjs/passport' -import { Strategy, type Profile, type VerifyCallback } from 'passport-42' -import { get } from 'https' -import { createWriteStream } from 'fs' +import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { PassportStrategy } from "@nestjs/passport"; +import { Strategy, type Profile, type VerifyCallback } from "passport-42"; +import { get } from "https"; +import { createWriteStream } from "fs"; -import { UsersService } from 'src/users/users.service' -import { User } from 'src/users/entity/user.entity' +import { UsersService } from "src/users/users.service"; +import { User } from "src/users/entity/user.entity"; @Injectable() -export class FtStrategy extends PassportStrategy(Strategy, '42') { - constructor ( +export class FtStrategy extends PassportStrategy(Strategy, "42") { + constructor( private readonly configService: ConfigService, private readonly usersService: UsersService ) { super({ - clientID: configService.get('FT_OAUTH_CLIENT_ID'), - clientSecret: configService.get('FT_OAUTH_CLIENT_SECRET'), - callbackURL: configService.get('FT_OAUTH_CALLBACK_URL'), - passReqToCallback: true - }) + clientID: configService.get("FT_OAUTH_CLIENT_ID"), + clientSecret: configService.get("FT_OAUTH_CLIENT_SECRET"), + callbackURL: configService.get("FT_OAUTH_CALLBACK_URL"), + passReqToCallback: true, + }); } - async validate ( + async validate( request: { session: { accessToken: string } }, accessToken: string, refreshToken: string, profile: Profile, cb: VerifyCallback ): Promise { - request.session.accessToken = accessToken - const ftId = profile.id as number - console.log('Validated ', profile.username) + request.session.accessToken = accessToken; + const ftId = profile.id as number; + console.log("Validated ", profile.username); if ((await this.usersService.findUser(ftId)) === null) { - const newUser = new User() - newUser.ftId = profile.id as number - newUser.username = profile.username - newUser.avatar = `${ftId}.jpg` - newUser.email = profile.emails[0].value - void this.usersService.create(newUser) - const file = createWriteStream(`avatars/${ftId}.jpg`) + const newUser = new User(); + newUser.ftId = profile.id as number; + newUser.username = profile.username; + newUser.avatar = `${ftId}.jpg`; + newUser.email = profile.emails[0].value; + void this.usersService.create(newUser); + const file = createWriteStream(`avatars/${ftId}.jpg`); get(profile._json.image.versions.small, function (response) { - response.pipe(file) - }) + response.pipe(file); + }); } - return cb(null, profile) + return cb(null, profile); } } diff --git a/back/volume/src/auth/auth.controller.ts b/back/volume/src/auth/auth.controller.ts index a380cad..fc30fcb 100644 --- a/back/volume/src/auth/auth.controller.ts +++ b/back/volume/src/auth/auth.controller.ts @@ -6,75 +6,75 @@ import { Res, Req, Post, - Body -} from '@nestjs/common' -import { Response, Request } from 'express' + Body, +} from "@nestjs/common"; +import { Response, Request } from "express"; -import { FtOauthGuard, AuthenticatedGuard } from './42-auth.guard' -import { Profile } from 'passport-42' -import { Profile42 } from './42.decorator' +import { FtOauthGuard, AuthenticatedGuard } from "./42-auth.guard"; +import { Profile } from "passport-42"; +import { Profile42 } from "./42.decorator"; -import { AuthService } from './auth.service' -import { UsersService } from 'src/users/users.service' +import { AuthService } from "./auth.service"; +import { UsersService } from "src/users/users.service"; const frontHost = - process.env.HOST !== undefined && process.env.HOST !== '' + process.env.HOST !== undefined && process.env.HOST !== "" ? process.env.HOST - : 'localhost' + : "localhost"; const frontPort = - process.env.PORT !== undefined && process.env.HOST !== '' + process.env.PORT !== undefined && process.env.HOST !== "" ? process.env.PORT - : '80' + : "80"; -@Controller('log') +@Controller("log") export class AuthController { - constructor ( + constructor( private readonly authService: AuthService, private readonly usersService: UsersService ) {} - @Get('in') + @Get("in") @UseGuards(FtOauthGuard) - ftAuth (): void {} + ftAuth(): void {} - @Get('inReturn') + @Get("inReturn") @UseGuards(FtOauthGuard) @Redirect(`http://${frontHost}:${frontPort}`) - ftAuthCallback ( + ftAuthCallback( @Res({ passthrough: true }) response: Response, - @Req() request: Request + @Req() request: Request ): any { - console.log('cookie:', request.cookies['connect.sid']) - response.cookie('connect.sid', request.cookies['connect.sid']) + console.log("cookie:", request.cookies["connect.sid"]); + response.cookie("connect.sid", request.cookies["connect.sid"]); } - @Get('/verify') + @Get("/verify") @UseGuards(AuthenticatedGuard) @Redirect(`http://${frontHost}:${frontPort}`) - async VerifyEmail (@Profile42() profile: Profile): Promise { - const ftId: number = profile.id - const user = await this.usersService.findUser(ftId) - if (user == null) throw new Error('User not found') - await this.authService.sendConfirmationEmail(user) + async VerifyEmail(@Profile42() profile: Profile): Promise { + const ftId: number = profile.id; + const user = await this.usersService.findUser(ftId); + if (user == null) throw new Error("User not found"); + await this.authService.sendConfirmationEmail(user); } - @Post('/verify') + @Post("/verify") @Redirect(`http://${frontHost}:${frontPort}`) - async Verify (@Body() body: any): Promise { - await this.authService.verifyAccount(body.code) + async Verify(@Body() body: any): Promise { + await this.authService.verifyAccount(body.code); } - @Get('profile') + @Get("profile") @UseGuards(AuthenticatedGuard) - profile (@Profile42() user: Profile): any { - return { user } + profile(@Profile42() user: Profile): any { + return { user }; } - @Get('out') + @Get("out") @Redirect(`http://${frontHost}:${frontPort}`) - logOut (@Req() req: Request): any { + logOut(@Req() req: Request): any { req.logOut(function (err) { - if (err != null) return err - }) + if (err != null) return err; + }); } } diff --git a/back/volume/src/auth/auth.module.ts b/back/volume/src/auth/auth.module.ts index d3715f0..1077eb4 100644 --- a/back/volume/src/auth/auth.module.ts +++ b/back/volume/src/auth/auth.module.ts @@ -1,23 +1,23 @@ -import { Module } from '@nestjs/common' -import { UsersModule } from 'src/users/users.module' -import { PassportModule } from '@nestjs/passport' -import { ConfigModule, ConfigService } from '@nestjs/config' -import { AuthController } from './auth.controller' -import { FtStrategy } from './42.strategy' -import { SessionSerializer } from './session.serializer' -import { JwtModule } from '@nestjs/jwt' -import { MailerModule } from '@nestjs-modules/mailer' -import { AuthService } from './auth.service' -import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter' +import { Module } from "@nestjs/common"; +import { UsersModule } from "src/users/users.module"; +import { PassportModule } from "@nestjs/passport"; +import { ConfigModule, ConfigService } from "@nestjs/config"; +import { AuthController } from "./auth.controller"; +import { FtStrategy } from "./42.strategy"; +import { SessionSerializer } from "./session.serializer"; +import { JwtModule } from "@nestjs/jwt"; +import { MailerModule } from "@nestjs-modules/mailer"; +import { AuthService } from "./auth.service"; +import { HandlebarsAdapter } from "@nestjs-modules/mailer/dist/adapters/handlebars.adapter"; const mailUser = - process.env.MAIL_USER !== null && process.env.MAIL_USER !== '' + process.env.MAIL_USER !== null && process.env.MAIL_USER !== "" ? process.env.MAIL_USER - : '' + : ""; const mailPass = - process.env.MAIL_PASSWORD !== null && process.env.MAIL_PASSWORD !== '' + process.env.MAIL_PASSWORD !== null && process.env.MAIL_PASSWORD !== "" ? process.env.MAIL_PASSWORD - : '' + : ""; @Module({ imports: [ @@ -26,29 +26,29 @@ const mailPass = ConfigModule.forRoot(), JwtModule.register({ secret: process.env.JWT_SECRET, - signOptions: { expiresIn: '60s' } + signOptions: { expiresIn: "60s" }, }), MailerModule.forRoot({ transport: { - service: 'gmail', + service: "gmail", auth: { user: mailUser, - pass: mailPass - } + pass: mailPass, + }, }, template: { - dir: 'src/auth/mails', + dir: "src/auth/mails", adapter: new HandlebarsAdapter(), options: { - strict: true - } + strict: true, + }, }, defaults: { - from: '"No Reply" vaganiwast@gmail.com' - } - }) + from: '"No Reply" vaganiwast@gmail.com', + }, + }), ], providers: [ConfigService, FtStrategy, SessionSerializer, AuthService], - controllers: [AuthController] + controllers: [AuthController], }) export class AuthModule {} diff --git a/back/volume/src/auth/auth.service.ts b/back/volume/src/auth/auth.service.ts index 0db7907..21b0a8b 100644 --- a/back/volume/src/auth/auth.service.ts +++ b/back/volume/src/auth/auth.service.ts @@ -1,48 +1,48 @@ -import { Injectable } from '@nestjs/common' -import { type User } from 'src/users/entity/user.entity' -import { UsersService } from 'src/users/users.service' -import { MailerService } from '@nestjs-modules/mailer' +import { Injectable } from "@nestjs/common"; +import { type User } from "src/users/entity/user.entity"; +import { UsersService } from "src/users/users.service"; +import { MailerService } from "@nestjs-modules/mailer"; @Injectable() export class AuthService { - constructor ( + constructor( private readonly usersService: UsersService, private readonly mailerService: MailerService ) {} - async sendConfirmedEmail (user: User): Promise { - const { email, username } = user + async sendConfirmedEmail(user: User): Promise { + const { email, username } = user; await this.mailerService.sendMail({ to: email, - subject: 'Welcome to ft_transcendence! Email Confirmed', - template: 'confirmed', + subject: "Welcome to ft_transcendence! Email Confirmed", + template: "confirmed", context: { username, - email - } - }) + email, + }, + }); } - async sendConfirmationEmail (user: User): Promise { - user.authToken = Math.floor(10000 + Math.random() * 90000).toString() - await this.usersService.save(user) + async sendConfirmationEmail(user: User): Promise { + user.authToken = Math.floor(10000 + Math.random() * 90000).toString(); + await this.usersService.save(user); await this.mailerService.sendMail({ to: user.email, - subject: 'Welcome to ft_transcendence! Confirm Email', - template: 'confirm', + subject: "Welcome to ft_transcendence! Confirm Email", + template: "confirm", context: { username: user.username, - code: user.authToken - } - }) + code: user.authToken, + }, + }); } - async verifyAccount (code: string): Promise { - const user = await this.usersService.findByCode(code) - user.authToken = '' - user.isVerified = true - await this.usersService.save(user) - await this.sendConfirmedEmail(user) - return true + async verifyAccount(code: string): Promise { + const user = await this.usersService.findByCode(code); + user.authToken = ""; + user.isVerified = true; + await this.usersService.save(user); + await this.sendConfirmedEmail(user); + return true; } } diff --git a/back/volume/src/auth/session.serializer.ts b/back/volume/src/auth/session.serializer.ts index 7698561..d6c9fa0 100644 --- a/back/volume/src/auth/session.serializer.ts +++ b/back/volume/src/auth/session.serializer.ts @@ -1,20 +1,20 @@ -import { Injectable } from '@nestjs/common' -import { PassportSerializer } from '@nestjs/passport' -import { type Profile } from 'passport-42' +import { Injectable } from "@nestjs/common"; +import { PassportSerializer } from "@nestjs/passport"; +import { type Profile } from "passport-42"; @Injectable() export class SessionSerializer extends PassportSerializer { - serializeUser ( + serializeUser( user: Profile, done: (err: Error | null, user: Profile) => void ): any { - done(null, user) + done(null, user); } - deserializeUser ( + deserializeUser( payload: Profile, done: (err: Error | null, user: Profile) => void ): any { - done(null, payload) + done(null, payload); } } diff --git a/back/volume/src/chat/chat.controller.ts b/back/volume/src/chat/chat.controller.ts index a774a0e..e487885 100644 --- a/back/volume/src/chat/chat.controller.ts +++ b/back/volume/src/chat/chat.controller.ts @@ -7,171 +7,211 @@ import { NotFoundException, Param, Post, - UseGuards -} from '@nestjs/common' -import { AuthenticatedGuard } from 'src/auth/42-auth.guard' -import { UsersService } from 'src/users/users.service' -import { ChatService } from './chat.service' + UseGuards, +} from "@nestjs/common"; +import { AuthenticatedGuard } from "src/auth/42-auth.guard"; +import { UsersService } from "src/users/users.service"; +import { ChatService } from "./chat.service"; -import { CreateChannelDto } from './dto/create-channel.dto' -import { IdDto, PasswordDto, MuteDto } from './dto/updateUser.dto' +import { CreateChannelDto } from "./dto/create-channel.dto"; +import { IdDto, PasswordDto, MuteDto } from "./dto/updateUser.dto"; -import type User from 'src/users/entity/user.entity' -import type Channel from './entity/channel.entity' -import { Profile42 } from 'src/auth/42.decorator' -import { Profile } from 'passport-42' +import type User from "src/users/entity/user.entity"; +import type Channel from "./entity/channel.entity"; +import { Profile42 } from "src/auth/42.decorator"; +import { Profile } from "passport-42"; -@Controller('channels') +@Controller("channels") export class ChatController { - constructor ( + constructor( private readonly channelService: ChatService, private readonly usersService: UsersService ) {} - @Post(':id/invite') + @Post(":id/invite") @UseGuards(AuthenticatedGuard) - async addUser ( - @Param('id') id: number, @Body() target: IdDto, - @Profile42() profile: Profile) { - const channel = await this.channelService.getFullChannel(id) - const user: User | null = await this.usersService.findUser(target.id) - if (user == null) throw new NotFoundException(`User #${target.id} not found`) - if (!await this.channelService.isUser(channel.id, +profile.id)) - throw new BadRequestException('You are not allowed to invite users to this channel') - if (await this.channelService.isUser(channel.id, target.id)) - throw new BadRequestException('User is already in this channel') - if (await this.channelService.isBanned(channel.id, target.id)) - throw new BadRequestException('User is banned from this channel') - channel.users.push(user) - this.channelService.save(channel) + async addUser( + @Param("id") id: number, + @Body() target: IdDto, + @Profile42() profile: Profile + ) { + const channel = await this.channelService.getFullChannel(id); + const user: User | null = await this.usersService.findUser(target.id); + if (user == null) { + throw new NotFoundException(`User #${target.id} not found`); + } + if (!(await this.channelService.isUser(channel.id, +profile.id))) { + throw new BadRequestException( + "You are not allowed to invite users to this channel" + ); + } + if (await this.channelService.isUser(channel.id, target.id)) { + throw new BadRequestException("User is already in this channel"); + } + if (await this.channelService.isBanned(channel.id, target.id)) { + throw new BadRequestException("User is banned from this channel"); + } + channel.users.push(user); + this.channelService.save(channel); } - @Delete(':id/kick') + @Delete(":id/kick") @UseGuards(AuthenticatedGuard) - async removeUser ( - @Param('id') id: number, @Body() target: IdDto, - @Profile42() profile: Profile) { - const channel = await this.channelService.getFullChannel(id) - if (!await this.channelService.isAdmin(channel.id, +profile.id)) - throw new BadRequestException('You are not allowed to kick users from this channel') - if (!await this.channelService.isUser(channel.id, target.id)) - throw new BadRequestException('User is not in this channel') - if (await this.channelService.isOwner(channel.id, target.id)) - throw new BadRequestException('You cannot kick the owner of the channel') + async removeUser( + @Param("id") id: number, + @Body() target: IdDto, + @Profile42() profile: Profile + ) { + const channel = await this.channelService.getFullChannel(id); + if (!(await this.channelService.isAdmin(channel.id, +profile.id))) { + throw new BadRequestException( + "You are not allowed to kick users from this channel" + ); + } + if (!(await this.channelService.isUser(channel.id, target.id))) { + throw new BadRequestException("User is not in this channel"); + } + if (await this.channelService.isOwner(channel.id, target.id)) { + throw new BadRequestException("You cannot kick the owner of the channel"); + } channel.users = channel.users.filter((usr: User) => { - return usr.ftId !== target.id - }) - this.channelService.save(channel) + return usr.ftId !== target.id; + }); + this.channelService.save(channel); } - @Post(':id/admin') + @Post(":id/admin") @UseGuards(AuthenticatedGuard) - async addAdmin ( - @Param('id') id: number, + async addAdmin( + @Param("id") id: number, @Body() target: IdDto, - @Profile42() profile: Profile) { - const channel = await this.channelService.getFullChannel(id) - const user: User | null = await this.usersService.findUser(target.id) - if (user == null) throw new NotFoundException(`User #${target.id} not found`) - if (!await this.channelService.isOwner(channel.id, +profile.id)) - throw new BadRequestException('You are not the owner of this channel') - if (!await this.channelService.isUser(channel.id, target.id)) - throw new BadRequestException('User is not in this channel') - if (await this.channelService.isAdmin(channel.id, target.id)) - throw new BadRequestException('User is already an admin of this channel') - channel.admins.push(user) - this.channelService.save(channel) + @Profile42() profile: Profile + ) { + const channel = await this.channelService.getFullChannel(id); + const user: User | null = await this.usersService.findUser(target.id); + if (user == null) { + throw new NotFoundException(`User #${target.id} not found`); + } + if (!(await this.channelService.isOwner(channel.id, +profile.id))) { + throw new BadRequestException("You are not the owner of this channel"); + } + if (!(await this.channelService.isUser(channel.id, target.id))) { + throw new BadRequestException("User is not in this channel"); + } + if (await this.channelService.isAdmin(channel.id, target.id)) { + throw new BadRequestException("User is already an admin of this channel"); + } + channel.admins.push(user); + this.channelService.save(channel); } - @Delete(':id/admin') + @Delete(":id/admin") @UseGuards(AuthenticatedGuard) - async removeAdmin ( - @Param('id') id: number, + async removeAdmin( + @Param("id") id: number, @Body() target: IdDto, - @Profile42() profile: Profile) { - const channel = await this.channelService.getFullChannel(id) - if (!await this.channelService.isOwner(channel.id, +profile.id)) - throw new BadRequestException('You are not the owner of this channel') - if (!await this.channelService.isAdmin(channel.id, target.id)) - throw new BadRequestException('User is not an admin of this channel') + @Profile42() profile: Profile + ) { + const channel = await this.channelService.getFullChannel(id); + if (!(await this.channelService.isOwner(channel.id, +profile.id))) { + throw new BadRequestException("You are not the owner of this channel"); + } + if (!(await this.channelService.isAdmin(channel.id, target.id))) { + throw new BadRequestException("User is not an admin of this channel"); + } channel.admins = channel.admins.filter((usr: User) => { - return usr.ftId !== target.id - }) - this.channelService.save(channel) + return usr.ftId !== target.id; + }); + this.channelService.save(channel); } - @Post(':id/ban') + @Post(":id/ban") @UseGuards(AuthenticatedGuard) - async addBan ( - @Param('id') id: number, + async addBan( + @Param("id") id: number, @Body() target: IdDto, - @Profile42() profile: Profile) { - const channel = await this.channelService.getFullChannel(id) - const user: User | null = await this.usersService.findUser(target.id) - if (user == null) throw new NotFoundException(`User #${target.id} not found`) - if (!await this.channelService.isAdmin(channel.id, +profile.id)) - throw new BadRequestException('You are not allowed to ban users from this channel') - if (await this.channelService.isOwner(channel.id, target.id)) - throw new BadRequestException('You cannot ban the owner of the channel') - if (await this.channelService.isBanned(channel.id, target.id)) - throw new BadRequestException('User is already banned from this channel') - channel.banned.push(user) - this.channelService.save(channel) + @Profile42() profile: Profile + ) { + const channel = await this.channelService.getFullChannel(id); + const user: User | null = await this.usersService.findUser(target.id); + if (user == null) { + throw new NotFoundException(`User #${target.id} not found`); + } + if (!(await this.channelService.isAdmin(channel.id, +profile.id))) { + throw new BadRequestException( + "You are not allowed to ban users from this channel" + ); + } + if (await this.channelService.isOwner(channel.id, target.id)) { + throw new BadRequestException("You cannot ban the owner of the channel"); + } + if (await this.channelService.isBanned(channel.id, target.id)) { + throw new BadRequestException("User is already banned from this channel"); + } + channel.banned.push(user); + this.channelService.save(channel); } - @Post(':id/mute') + @Post(":id/mute") @UseGuards(AuthenticatedGuard) - async addMute ( - @Param('id') id: number, - @Body() mute: MuteDto, // [userId, duration] - @Profile42() profile: Profile) { - const channel = await this.channelService.getFullChannel(id) - const user: User | null = await this.usersService.findUser(mute.data[0]) - if (user == null) throw new NotFoundException(`User #${mute.data[0]} not found`) - if (!await this.channelService.isAdmin(channel.id, +profile.id)) - throw new BadRequestException('You are not allowed to mute users from this channel') - if (await this.channelService.isOwner(channel.id, mute.data[0])) - throw new BadRequestException('You cannot mute the owner of the channel') - if (await this.channelService.getMuteDuration(channel.id, mute.data[0]) > 0) - throw new BadRequestException('User is already muted from this channel') - let newMute: Array = [mute.data[0], Date.now() + mute.data[1] * 1000] - channel.muted.push(newMute) - this.channelService.save(channel) + async addMute( + @Param("id") id: number, + @Body() mute: MuteDto, // [userId, duration] + @Profile42() profile: Profile + ) { + const channel = await this.channelService.getFullChannel(id); + const user: User | null = await this.usersService.findUser(mute.data[0]); + if (user == null) { + throw new NotFoundException(`User #${mute.data[0]} not found`); + } + if (!(await this.channelService.isAdmin(channel.id, +profile.id))) { + throw new BadRequestException( + "You are not allowed to mute users from this channel" + ); + } + if (await this.channelService.isOwner(channel.id, mute.data[0])) { + throw new BadRequestException("You cannot mute the owner of the channel"); + } + if ( + (await this.channelService.getMuteDuration(channel.id, mute.data[0])) > 0 + ) { + throw new BadRequestException("User is already muted from this channel"); + } + const newMute: number[] = [mute.data[0], Date.now() + mute.data[1] * 1000]; + channel.muted.push(newMute); + this.channelService.save(channel); } - @Delete(':id') + @Delete(":id") @UseGuards(AuthenticatedGuard) - async deleteChannel ( - @Profile42() profile: Profile, - @Param('id') id: number - ) { - if (!await this.channelService.isOwner(id, +profile.id)) - throw new BadRequestException('You are not the owner of this channel') - await this.channelService.removeChannel(id) - return + async deleteChannel(@Profile42() profile: Profile, @Param("id") id: number) { + if (!(await this.channelService.isOwner(id, +profile.id))) { + throw new BadRequestException("You are not the owner of this channel"); + } + await this.channelService.removeChannel(id); } - @Post(':id/password') + @Post(":id/password") @UseGuards(AuthenticatedGuard) - async updatePassword ( + async updatePassword( @Profile42() profile: Profile, - @Param('id') id: number, + @Param("id") id: number, @Body() data: PasswordDto ) { - if (await this.channelService.isOwner(id, +profile.id)) - throw new BadRequestException('You are not the owner of this channel') - await this.channelService.updatePassword(id, data.password) + if (await this.channelService.isOwner(id, +profile.id)) { + throw new BadRequestException("You are not the owner of this channel"); + } + await this.channelService.updatePassword(id, data.password); } - @Get() @UseGuards(AuthenticatedGuard) - async getChannelsForUser (@Profile42() profile: Profile): Promise { - return await this.channelService.getChannelsForUser(+profile.id) + async getChannelsForUser(@Profile42() profile: Profile): Promise { + return await this.channelService.getChannelsForUser(+profile.id); } @Post() - async createChannel (@Body() channel: CreateChannelDto) { - return await this.channelService.createChannel(channel) + async createChannel(@Body() channel: CreateChannelDto) { + return await this.channelService.createChannel(channel); } } diff --git a/back/volume/src/chat/chat.gateway.ts b/back/volume/src/chat/chat.gateway.ts index 8128b2f..8904838 100644 --- a/back/volume/src/chat/chat.gateway.ts +++ b/back/volume/src/chat/chat.gateway.ts @@ -4,40 +4,39 @@ import { SubscribeMessage, WebSocketGateway, WebSocketServer, - WsException -} from '@nestjs/websockets' -import { Socket, Server } from 'socket.io' + WsException, +} from "@nestjs/websockets"; +import { Socket, Server } from "socket.io"; // import { User } from 'users/user.entity'; -import { UsersService } from 'src/users/users.service' -import { BadRequestException } from '@nestjs/common' -import { ChatService } from './chat.service' -import Message from './entity/message.entity' -import * as bcrypt from 'bcrypt' -import { MessageService } from './message.service' -import { type User } from 'src/users/entity/user.entity' -import { CreateMessageDto } from './dto/create-message.dto' -import { InjectRepository } from '@nestjs/typeorm' -import { Repository } from 'typeorm' -import ConnectedUser from './entity/connection.entity' -import { ConnectionDto } from './dto/connection.dto' +import { UsersService } from "src/users/users.service"; +import { BadRequestException } from "@nestjs/common"; +import { ChatService } from "./chat.service"; +import type Message from "./entity/message.entity"; +import * as bcrypt from "bcrypt"; +import { MessageService } from "./message.service"; +import { type User } from "src/users/entity/user.entity"; +import { CreateMessageDto } from "./dto/create-message.dto"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; +import ConnectedUser from "./entity/connection.entity"; +import { ConnectionDto } from "./dto/connection.dto"; @WebSocketGateway({ - cors: { origin: /^(http|ws):\/\/localhost(:\d+)?$/ } + cors: { origin: /^(http|ws):\/\/localhost(:\d+)?$/ }, }) export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { @WebSocketServer() - server: Server + server: Server; - constructor ( + constructor( private readonly userService: UsersService, private readonly messageService: MessageService, private readonly chatService: ChatService, @InjectRepository(ConnectedUser) private readonly connectedUserRepository: Repository - ) { - } + ) {} - async handleConnection (socket: Socket): Promise { + async handleConnection(socket: Socket): Promise { // console.log(socket.handshake.headers) // const cookie = socket.handshake.headers.cookie as string // const { authentication: authenticationToken } = parse(cookie) @@ -50,69 +49,76 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { // return user } - handleDisconnect (socket: Socket): void { - socket.disconnect() + handleDisconnect(socket: Socket): void { + socket.disconnect(); } - @SubscribeMessage('joinChannel') - async onJoinChannel (socket: Socket, connect: ConnectionDto): Promise { - const channel = await this.chatService.getChannel(connect.ChannelId) + @SubscribeMessage("joinChannel") + async onJoinChannel(socket: Socket, connect: ConnectionDto): Promise { + const channel = await this.chatService.getChannel(connect.ChannelId); if (channel.banned.find((e) => e.id == connect.UserId) != null) { - throw new WsException('You are banned from entering this channel') + throw new WsException("You are banned from entering this channel"); } - const user = (await this.userService.findUser(connect.UserId)) as User + const user = (await this.userService.findUser(connect.UserId)) as User; if ( channel.users.find((e) => e.id === user.id) == null && - channel.password !== '' + channel.password !== "" ) { if (!(await bcrypt.compare(channel.password, connect.pwd))) { - throw new BadRequestException() + throw new BadRequestException(); } - } else await this.chatService.addUserToChannel(channel, user) + } else await this.chatService.addUserToChannel(channel, user); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// { - const conUser = new ConnectedUser() - conUser.user = user - conUser.channel = channel - conUser.socket = socket.id - await this.connectedUserRepository.save(conUser) + const conUser = new ConnectedUser(); + conUser.user = user; + conUser.channel = channel; + conUser.socket = socket.id; + await this.connectedUserRepository.save(conUser); } const messages = await this.messageService.findMessagesInChannelForUser( channel, user - ) - this.server.to(socket.id).emit('messages', messages) - await socket.join(channel.name) + ); + this.server.to(socket.id).emit("messages", messages); + await socket.join(channel.name); } - @SubscribeMessage('leaveChannel') - async onLeaveChannel (socket: Socket): Promise { - const id = socket.id as any - await this.connectedUserRepository.delete({ socket: id }) - socket.disconnect() + @SubscribeMessage("leaveChannel") + async onLeaveChannel(socket: Socket): Promise { + const id = socket.id as any; + await this.connectedUserRepository.delete({ socket: id }); + socket.disconnect(); } - @SubscribeMessage('addMessage') - async onAddMessage (socket: Socket, message: CreateMessageDto): Promise { - const channel = await this.chatService.getChannel(message.ChannelId) - if (await this.chatService.getMuteDuration(channel.id, message.UserId) > 0) { - throw new WsException('You are muted') + @SubscribeMessage("addMessage") + async onAddMessage(socket: Socket, message: CreateMessageDto): Promise { + const channel = await this.chatService.getChannel(message.ChannelId); + if ( + (await this.chatService.getMuteDuration(channel.id, message.UserId)) > 0 + ) { + throw new WsException("You are muted"); } const createdMessage: Message = await this.messageService.createMessage( message - ) - socket.in(channel.name).emit('newMessage', createdMessage) + ); + socket.in(channel.name).emit("newMessage", createdMessage); } - @SubscribeMessage('kickUser') - async onKickUser (socket: Socket, chan: number, from: number, to: number): Promise { - const channel = await this.chatService.getChannel(chan) + @SubscribeMessage("kickUser") + async onKickUser( + socket: Socket, + chan: number, + from: number, + to: number + ): Promise { + const channel = await this.chatService.getChannel(chan); if ( channel.owner.id !== from || channel.admins.find((e) => e.id === from) == null ) { - throw new WsException('You do not have the required privileges') + throw new WsException("You do not have the required privileges"); } - await this.onLeaveChannel(socket) + await this.onLeaveChannel(socket); } } diff --git a/back/volume/src/chat/chat.module.ts b/back/volume/src/chat/chat.module.ts index a64dbdf..4abae63 100644 --- a/back/volume/src/chat/chat.module.ts +++ b/back/volume/src/chat/chat.module.ts @@ -1,16 +1,16 @@ -import { forwardRef, Module } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' +import { forwardRef, Module } from "@nestjs/common"; +import { TypeOrmModule } from "@nestjs/typeorm"; -import { AuthModule } from 'src/auth/auth.module' -import { UsersModule } from 'src/users/users.module' -import { ChatGateway } from './chat.gateway' -import { ChatController } from './chat.controller' -import { ChatService } from './chat.service' -import { MessageService } from './message.service' +import { AuthModule } from "src/auth/auth.module"; +import { UsersModule } from "src/users/users.module"; +import { ChatGateway } from "./chat.gateway"; +import { ChatController } from "./chat.controller"; +import { ChatService } from "./chat.service"; +import { MessageService } from "./message.service"; -import Channel from './entity/channel.entity' -import Message from './entity/message.entity' -import ConnectedUser from './entity/connection.entity' +import Channel from "./entity/channel.entity"; +import Message from "./entity/message.entity"; +import ConnectedUser from "./entity/connection.entity"; @Module({ imports: [ @@ -20,7 +20,6 @@ import ConnectedUser from './entity/connection.entity' ], controllers: [ChatController], providers: [ChatService, ChatGateway, MessageService], - exports: [ChatService] - + exports: [ChatService], }) export class ChatModule {} diff --git a/back/volume/src/chat/chat.service.ts b/back/volume/src/chat/chat.service.ts index 3179232..bfd25b6 100644 --- a/back/volume/src/chat/chat.service.ts +++ b/back/volume/src/chat/chat.service.ts @@ -1,151 +1,173 @@ -import { Inject, Injectable, NotFoundException } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { Repository } from 'typeorm' +import { Inject, Injectable, NotFoundException } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; -import { type CreateChannelDto } from './dto/create-channel.dto' -import { UsersService } from 'src/users/users.service' +import { type CreateChannelDto } from "./dto/create-channel.dto"; +import { UsersService } from "src/users/users.service"; -import type User from 'src/users/entity/user.entity' -import Channel from './entity/channel.entity' -import { Cron } from '@nestjs/schedule' +import type User from "src/users/entity/user.entity"; +import Channel from "./entity/channel.entity"; +import { Cron } from "@nestjs/schedule"; @Injectable() export class ChatService { - constructor ( + constructor( @InjectRepository(Channel) private readonly ChannelRepository: Repository, private readonly usersService: UsersService ) {} - async createChannel (channel: CreateChannelDto): Promise { - const user: User | null = await this.usersService.findUser(channel.owner) - if (user == null) - throw new NotFoundException(`User #${channel.owner} not found`) - const newChannel = new Channel() - newChannel.owner = user - newChannel.users = [user] - newChannel.admins = [user] - newChannel.name = channel.name - newChannel.isPrivate = channel.isPrivate - newChannel.password = channel.password - return await this.ChannelRepository.save(newChannel) + async createChannel(channel: CreateChannelDto): Promise { + const user: User | null = await this.usersService.findUser(channel.owner); + if (user == null) { + throw new NotFoundException(`User #${channel.owner} not found`); + } + const newChannel = new Channel(); + newChannel.owner = user; + newChannel.users = [user]; + newChannel.admins = [user]; + newChannel.name = channel.name; + newChannel.isPrivate = channel.isPrivate; + newChannel.password = channel.password; + return await this.ChannelRepository.save(newChannel); } - - - async updatePassword (id: number, password: string) { - let channel: Channel | null = await this.ChannelRepository.findOneBy({id}) - if (channel === null) { throw new NotFoundException(`Channel #${id} not found`) } - channel.password = password - await this.ChannelRepository.save(channel) + async updatePassword(id: number, password: string) { + const channel: Channel | null = await this.ChannelRepository.findOneBy({ + id, + }); + if (channel === null) { + throw new NotFoundException(`Channel #${id} not found`); + } + channel.password = password; + await this.ChannelRepository.save(channel); } - async getChannelsForUser (ftId: number): Promise { - let rooms: Channel[] = [] + async getChannelsForUser(ftId: number): Promise { + let rooms: Channel[] = []; rooms = [ - ...(await this.ChannelRepository.createQueryBuilder('room') - .where('room.isPrivate = false') - .getMany()) - ] + ...(await this.ChannelRepository.createQueryBuilder("room") + .where("room.isPrivate = false") + .getMany()), + ]; rooms = [ ...rooms, - ...(await this.ChannelRepository.createQueryBuilder('room') - .innerJoin('room.users', 'users') - .where('room.isPrivate = true') - .andWhere('users.ftId = :ftId', { ftId }) - .getMany()) - ] - return rooms + ...(await this.ChannelRepository.createQueryBuilder("room") + .innerJoin("room.users", "users") + .where("room.isPrivate = true") + .andWhere("users.ftId = :ftId", { ftId }) + .getMany()), + ]; + return rooms; } - @Cron('*/6 * * * * *') + @Cron("*/6 * * * * *") async updateMutelists(): Promise { - let channels = await this.ChannelRepository.find({}) + const channels = await this.ChannelRepository.find({}); channels.forEach((channel) => { - channel.muted = channel.muted.filter((data) => { return (data[0] - Date.now()) > 0;}); + channel.muted = channel.muted.filter((data) => { + return data[0] - Date.now() > 0; + }); this.ChannelRepository.save(channel); - }) + }); } - async addUserToChannel (channel: Channel, user: User): Promise { - channel.owner = user - return await this.ChannelRepository.save(channel) + async addUserToChannel(channel: Channel, user: User): Promise { + channel.owner = user; + return await this.ChannelRepository.save(channel); } - async getChannel (id: number): Promise { - const channel = await this.ChannelRepository.findOneBy({ id }) - if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) } - return channel + async getChannel(id: number): Promise { + const channel = await this.ChannelRepository.findOneBy({ id }); + if (channel == null) { + throw new NotFoundException(`Channel #${id} not found`); + } + return channel; } - async getFullChannel (id: number): Promise { + async getFullChannel(id: number): Promise { const channel = await this.ChannelRepository.findOne({ where: { id }, - relations: ['users', 'admins', 'banned', 'muted', 'owner'] - }) - if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) } - return channel + relations: ["users", "admins", "banned", "muted", "owner"], + }); + if (channel == null) { + throw new NotFoundException(`Channel #${id} not found`); + } + return channel; } - async update (channel: Channel) { - await this.ChannelRepository.update(channel.id, channel) + async update(channel: Channel) { + await this.ChannelRepository.update(channel.id, channel); } - async save (channel: Channel) { - await this.ChannelRepository.save(channel) + async save(channel: Channel) { + await this.ChannelRepository.save(channel); } - async removeChannel (channelId: number) { - await this.ChannelRepository.delete(channelId) + async removeChannel(channelId: number) { + await this.ChannelRepository.delete(channelId); } - async isOwner (id: number, userId: number): Promise { + async isOwner(id: number, userId: number): Promise { const channel = await this.ChannelRepository.findOne({ where: { id }, - relations: { owner: true } - }) - if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) } - return channel.owner.ftId === userId + relations: { owner: true }, + }); + if (channel == null) { + throw new NotFoundException(`Channel #${id} not found`); + } + return channel.owner.ftId === userId; } - async isAdmin (id: number, userId: number): Promise { + async isAdmin(id: number, userId: number): Promise { const channel = await this.ChannelRepository.findOne({ where: { id }, - relations: { admins: true } - }) - if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) } - return channel.admins.findIndex((user) => user.ftId === userId) != -1 + relations: { admins: true }, + }); + if (channel == null) { + throw new NotFoundException(`Channel #${id} not found`); + } + return channel.admins.findIndex((user) => user.ftId === userId) != -1; } - async isUser (id: number, userId: number): Promise { + async isUser(id: number, userId: number): Promise { const channel = await this.ChannelRepository.findOne({ where: { id }, - relations: { users: true } - }) - if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) } - return channel.users.findIndex((user) => user.ftId === userId) != -1 + relations: { users: true }, + }); + if (channel == null) { + throw new NotFoundException(`Channel #${id} not found`); + } + return channel.users.findIndex((user) => user.ftId === userId) != -1; } - async isBanned (id: number, userId: number): Promise { + async isBanned(id: number, userId: number): Promise { const channel = await this.ChannelRepository.findOne({ where: { id }, - relations: { banned: true } - }) - if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) } - return channel.banned.findIndex((user) => user.ftId === userId) != -1 + relations: { banned: true }, + }); + if (channel == null) { + throw new NotFoundException(`Channel #${id} not found`); + } + return channel.banned.findIndex((user) => user.ftId === userId) != -1; } - async getMuteDuration (id: number, userId: number): Promise { + async getMuteDuration(id: number, userId: number): Promise { const channel = await this.ChannelRepository.findOne({ where: { id }, - relations: { muted: true } - }) - if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) } - - const mutation: Array | undefined = channel.muted.find((mutation) => mutation[0] === userId) - if (mutation == null) { return 0 } - return mutation[1] - + relations: { muted: true }, + }); + if (channel == null) { + throw new NotFoundException(`Channel #${id} not found`); + } + + const mutation: number[] | undefined = channel.muted.find( + (mutation) => mutation[0] === userId + ); + if (mutation == null) { + return 0; + } + return mutation[1]; } } diff --git a/back/volume/src/chat/dto/connection.dto.ts b/back/volume/src/chat/dto/connection.dto.ts index c70f38c..551e545 100644 --- a/back/volume/src/chat/dto/connection.dto.ts +++ b/back/volume/src/chat/dto/connection.dto.ts @@ -1,13 +1,13 @@ -import { IsNumber, IsOptional, IsString } from 'class-validator' +import { IsNumber, IsOptional, IsString } from "class-validator"; export class ConnectionDto { @IsNumber() - UserId: number + UserId: number; @IsNumber() - ChannelId: number + ChannelId: number; @IsString() @IsOptional() - pwd: string + pwd: string; } diff --git a/back/volume/src/chat/dto/create-channel.dto.ts b/back/volume/src/chat/dto/create-channel.dto.ts index af1f03a..042e6ce 100644 --- a/back/volume/src/chat/dto/create-channel.dto.ts +++ b/back/volume/src/chat/dto/create-channel.dto.ts @@ -1,28 +1,28 @@ -import { Transform } from 'class-transformer' +import { Transform } from "class-transformer"; import { IsPositive, IsAlpha, IsString, IsOptional, IsNumber, - IsBoolean -} from 'class-validator' + IsBoolean, +} from "class-validator"; export class CreateChannelDto { @IsOptional() @IsPositive() - id: number + id: number; @IsString() - name: string + name: string; @IsNumber() - owner: number + owner: number; @IsOptional() - password: string + password: string; @IsBoolean() - @Transform(({ value }) => value === 'true') - isPrivate: boolean + @Transform(({ value }) => value === "true") + isPrivate: boolean; } diff --git a/back/volume/src/chat/dto/create-message.dto.ts b/back/volume/src/chat/dto/create-message.dto.ts index 56419a0..167d1ea 100644 --- a/back/volume/src/chat/dto/create-message.dto.ts +++ b/back/volume/src/chat/dto/create-message.dto.ts @@ -1,12 +1,12 @@ -import { IsNumber, IsString } from 'class-validator' +import { IsNumber, IsString } from "class-validator"; export class CreateMessageDto { @IsString() - text: string + text: string; @IsNumber() - UserId: number + UserId: number; @IsNumber() - ChannelId: number + ChannelId: number; } diff --git a/back/volume/src/chat/dto/update-channel.dto.ts b/back/volume/src/chat/dto/update-channel.dto.ts index c9cb3a3..6b4da58 100644 --- a/back/volume/src/chat/dto/update-channel.dto.ts +++ b/back/volume/src/chat/dto/update-channel.dto.ts @@ -1,30 +1,30 @@ -import { PartialType } from '@nestjs/mapped-types' -import { CreateChannelDto } from './create-channel.dto' -import { IsNumber, IsOptional, IsString } from 'class-validator' +import { PartialType } from "@nestjs/mapped-types"; +import { CreateChannelDto } from "./create-channel.dto"; +import { IsNumber, IsOptional, IsString } from "class-validator"; export class UpdateChannelDto extends PartialType(CreateChannelDto) { - id: number + id: number; @IsOptional() @IsNumber() - users: [number] + users: [number]; @IsOptional() @IsNumber() - messages: [number] + messages: [number]; @IsOptional() @IsNumber() - owners: [number] // user id + owners: [number]; // user id @IsOptional() @IsNumber() - banned: [number] // user id + banned: [number]; // user id @IsOptional() @IsNumber() - muted: [number] // user id + muted: [number]; // user id @IsString() @IsOptional() - password: string + password: string; } diff --git a/back/volume/src/chat/dto/updateUser.dto.ts b/back/volume/src/chat/dto/updateUser.dto.ts index bbc0350..f1ef1b9 100644 --- a/back/volume/src/chat/dto/updateUser.dto.ts +++ b/back/volume/src/chat/dto/updateUser.dto.ts @@ -1,16 +1,15 @@ - -import { IsNumber, IsString} from 'class-validator' +import { IsNumber, IsString } from "class-validator"; export class IdDto { @IsNumber() - id: number + id: number; } export class PasswordDto { @IsString() - password: string + password: string; } export class MuteDto { - data: Array + data: number[]; } diff --git a/back/volume/src/chat/entity/channel.entity.ts b/back/volume/src/chat/entity/channel.entity.ts index 47eabfd..4b9ecc9 100644 --- a/back/volume/src/chat/entity/channel.entity.ts +++ b/back/volume/src/chat/entity/channel.entity.ts @@ -8,54 +8,54 @@ import { ManyToOne, OneToMany, OneToOne, - PrimaryGeneratedColumn -} from 'typeorm' -import User from 'src/users/entity/user.entity' -import Message from './message.entity' -import * as bcrypt from 'bcrypt' + PrimaryGeneratedColumn, +} from "typeorm"; +import User from "src/users/entity/user.entity"; +import Message from "./message.entity"; +import * as bcrypt from "bcrypt"; @Entity() export default class Channel { @PrimaryGeneratedColumn() - id: number + id: number; @Column() - name: string + name: string; @Column({ default: false }) - isPrivate: boolean + isPrivate: boolean; - @Column({ select: false, default: '' }) - password: string + @Column({ select: false, default: "" }) + password: string; @BeforeInsert() - async hashPassword () { - if (this.password === '') return + async hashPassword() { + if (this.password === "") return; this.password = await bcrypt.hash( this.password, Number(process.env.HASH_SALT) - ) + ); } @ManyToMany(() => User) @JoinTable() - users: User[] + users: User[]; @OneToMany(() => Message, (message: Message) => message.channel) - messages: Message[] + messages: Message[]; @ManyToOne(() => User) @JoinColumn() - owner: User + owner: User; @ManyToMany(() => User) @JoinTable() - admins: User[] + admins: User[]; @ManyToMany(() => User) // refuse connection @JoinTable() - banned: User[] + banned: User[]; - @Column('text', {array: true}) - muted: number[][] + @Column("text", { array: true }) + muted: number[][]; } diff --git a/back/volume/src/chat/entity/connection.entity.ts b/back/volume/src/chat/entity/connection.entity.ts index 270ee37..16dc4d0 100644 --- a/back/volume/src/chat/entity/connection.entity.ts +++ b/back/volume/src/chat/entity/connection.entity.ts @@ -1,17 +1,23 @@ -import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from 'typeorm' +import { + Column, + Entity, + JoinColumn, + OneToOne, + PrimaryGeneratedColumn, +} from "typeorm"; -import Channel from './channel.entity' -import User from 'src/users/entity/user.entity' +import Channel from "./channel.entity"; +import User from "src/users/entity/user.entity"; @Entity() export default class ConnectedUser { @OneToOne(() => User) - user: User + user: User; @OneToOne(() => Channel) @JoinColumn() - channel: Channel + channel: Channel; @PrimaryGeneratedColumn() - socket: string + socket: string; } diff --git a/back/volume/src/chat/entity/dm.entity.ts b/back/volume/src/chat/entity/dm.entity.ts index d1a344c..9181791 100644 --- a/back/volume/src/chat/entity/dm.entity.ts +++ b/back/volume/src/chat/entity/dm.entity.ts @@ -1,15 +1,15 @@ -import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm' -import Message from './message.entity' -import type User from 'src/users/entity/user.entity' +import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import Message from "./message.entity"; +import type User from "src/users/entity/user.entity"; @Entity() export class Channel { @PrimaryGeneratedColumn() - id: number + id: number; @Column() - users: User[] + users: User[]; @OneToMany(() => Message, (message) => message.channel) - messages: Message[] + messages: Message[]; } diff --git a/back/volume/src/chat/entity/message.entity.ts b/back/volume/src/chat/entity/message.entity.ts index e44b4a3..c705c23 100644 --- a/back/volume/src/chat/entity/message.entity.ts +++ b/back/volume/src/chat/entity/message.entity.ts @@ -5,27 +5,27 @@ import { JoinColumn, JoinTable, ManyToOne, - PrimaryGeneratedColumn -} from 'typeorm' -import User from 'src/users/entity/user.entity' -import Channel from './channel.entity' + PrimaryGeneratedColumn, +} from "typeorm"; +import User from "src/users/entity/user.entity"; +import Channel from "./channel.entity"; @Entity() export default class Message { @PrimaryGeneratedColumn() - id: number + id: number; @Column() - text: string + text: string; @ManyToOne(() => User) @JoinColumn() - author: User + author: User; @ManyToOne(() => Channel, (channel) => channel.messages, { cascade: true }) @JoinTable() - channel: Channel + channel: Channel; @CreateDateColumn() - created_at: Date + created_at: Date; } diff --git a/back/volume/src/chat/message.service.ts b/back/volume/src/chat/message.service.ts index 7d25f26..d329e24 100644 --- a/back/volume/src/chat/message.service.ts +++ b/back/volume/src/chat/message.service.ts @@ -1,13 +1,13 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { ChatService } from './chat.service'; -import { UsersService } from 'src/users/users.service'; +import { Injectable } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; +import { ChatService } from "./chat.service"; +import { UsersService } from "src/users/users.service"; -import { type CreateMessageDto } from './dto/create-message.dto'; -import User from 'src/users/entity/user.entity'; -import Channel from './entity/channel.entity'; -import Message from './entity/message.entity'; +import { type CreateMessageDto } from "./dto/create-message.dto"; +import type User from "src/users/entity/user.entity"; +import type Channel from "./entity/channel.entity"; +import Message from "./entity/message.entity"; @Injectable() export class MessageService { @@ -15,7 +15,7 @@ export class MessageService { @InjectRepository(Message) private readonly MessageRepository: Repository, private readonly channelService: ChatService, - private readonly usersService: UsersService, + private readonly usersService: UsersService ) {} async createMessage(message: CreateMessageDto): Promise { @@ -25,17 +25,17 @@ export class MessageService { msg.author = (await this.usersService.findUser(message.UserId)) as User; msg.channel.messages.push(msg); return await this.MessageRepository.save( - this.MessageRepository.create(msg), + this.MessageRepository.create(msg) ); } async findMessagesInChannelForUser( channel: Channel, - user: User, + user: User ): Promise { - return await this.MessageRepository.createQueryBuilder('message') - .where('message.channel = :chan', { chan: channel }) - .andWhere('message.author NOT IN (:...blocked)', { + return await this.MessageRepository.createQueryBuilder("message") + .where("message.channel = :chan", { chan: channel }) + .andWhere("message.author NOT IN (:...blocked)", { blocked: user.blocked, }) .getMany(); diff --git a/back/volume/src/main.ts b/back/volume/src/main.ts index 88efdea..dc87b9d 100644 --- a/back/volume/src/main.ts +++ b/back/volume/src/main.ts @@ -1,45 +1,45 @@ -import { InternalServerErrorException, Logger } from '@nestjs/common' -import { NestFactory } from '@nestjs/core' -import { AppModule } from './app.module' -import * as session from 'express-session' -import * as passport from 'passport' -import { type NestExpressApplication } from '@nestjs/platform-express' -import * as cookieParser from 'cookie-parser' -import { IoAdapter } from '@nestjs/platform-socket.io' +import { InternalServerErrorException, Logger } from "@nestjs/common"; +import { NestFactory } from "@nestjs/core"; +import { AppModule } from "./app.module"; +import * as session from "express-session"; +import * as passport from "passport"; +import { type NestExpressApplication } from "@nestjs/platform-express"; +import * as cookieParser from "cookie-parser"; +import { IoAdapter } from "@nestjs/platform-socket.io"; -async function bootstrap (): Promise { - const logger = new Logger() - const app = await NestFactory.create(AppModule) +async function bootstrap(): Promise { + const logger = new Logger(); + const app = await NestFactory.create(AppModule); const port = - process.env.BACK_PORT !== undefined && process.env.BACK_PORT !== '' + process.env.BACK_PORT !== undefined && process.env.BACK_PORT !== "" ? +process.env.BACK_PORT - : 3001 + : 3001; const cors = { origin: /^(http|ws):\/\/localhost(:\d+)?$/, - methods: 'GET, HEAD, PUT, PATCH, POST, DELETE, OPTIONS', + methods: "GET, HEAD, PUT, PATCH, POST, DELETE, OPTIONS", preflightContinue: false, optionsSuccessStatus: 204, credentials: true, - allowedHeaders: ['Accept', 'Content-Type', 'Authorization'] - } + allowedHeaders: ["Accept", "Content-Type", "Authorization"], + }; app.use( session({ resave: false, saveUninitialized: false, secret: - process.env.JWT_SECRET !== undefined && process.env.JWT_SECRET !== '' + process.env.JWT_SECRET !== undefined && process.env.JWT_SECRET !== "" ? process.env.JWT_SECRET - : 'secret' + : "secret", }) - ) - app.use(cookieParser()) - app.use(passport.initialize()) - app.use(passport.session()) - app.enableCors(cors) - app.useWebSocketAdapter(new IoAdapter(app)) - await app.listen(port) - logger.log(`Application listening on port ${port}`) + ); + app.use(cookieParser()); + app.use(passport.initialize()); + app.use(passport.session()); + app.enableCors(cors); + app.useWebSocketAdapter(new IoAdapter(app)); + await app.listen(port); + logger.log(`Application listening on port ${port}`); } bootstrap().catch((e) => { - throw new InternalServerErrorException(e) -}) + throw new InternalServerErrorException(e); +}); diff --git a/back/volume/src/pong/dtos/GameCreationDtoValidated.ts b/back/volume/src/pong/dtos/GameCreationDtoValidated.ts index 3b55da4..d68bbac 100644 --- a/back/volume/src/pong/dtos/GameCreationDtoValidated.ts +++ b/back/volume/src/pong/dtos/GameCreationDtoValidated.ts @@ -1,4 +1,4 @@ -import { Type } from 'class-transformer' +import { Type } from "class-transformer"; import { ArrayMaxSize, ArrayMinSize, @@ -7,32 +7,32 @@ import { IsString, Max, Min, - ValidateNested -} from 'class-validator' + ValidateNested, +} from "class-validator"; import { DEFAULT_BALL_INITIAL_SPEED, - DEFAULT_MAX_BALL_SPEED -} from '../game/constants' -import { MapDtoValidated } from './MapDtoValidated' + DEFAULT_MAX_BALL_SPEED, +} from "../game/constants"; +import { MapDtoValidated } from "./MapDtoValidated"; export class GameCreationDtoValidated { @IsString({ each: true }) @ArrayMaxSize(2) @ArrayMinSize(2) - playerNames!: string[] + playerNames!: string[]; @IsNotEmptyObject() @ValidateNested() @Type(() => MapDtoValidated) - map!: MapDtoValidated + map!: MapDtoValidated; @IsNumber() @Min(DEFAULT_BALL_INITIAL_SPEED.x) @Max(DEFAULT_MAX_BALL_SPEED.x) - initialBallSpeedX!: number + initialBallSpeedX!: number; @IsNumber() @Min(DEFAULT_BALL_INITIAL_SPEED.y) @Max(DEFAULT_MAX_BALL_SPEED.y) - initialBallSpeedY!: number + initialBallSpeedY!: number; } diff --git a/back/volume/src/pong/dtos/GameInfo.ts b/back/volume/src/pong/dtos/GameInfo.ts index 95f36f7..818246e 100644 --- a/back/volume/src/pong/dtos/GameInfo.ts +++ b/back/volume/src/pong/dtos/GameInfo.ts @@ -1,12 +1,12 @@ -import { type Point, type Rect } from '../game/utils' +import { type Point, type Rect } from "../game/utils"; export class GameInfo { - mapSize!: Point - yourPaddleIndex!: number - gameId!: string - walls!: Rect[] - paddleSize!: Point - ballSize!: Point - winScore!: number - ranked!: boolean + mapSize!: Point; + yourPaddleIndex!: number; + gameId!: string; + walls!: Rect[]; + paddleSize!: Point; + ballSize!: Point; + winScore!: number; + ranked!: boolean; } diff --git a/back/volume/src/pong/dtos/GameUpdate.ts b/back/volume/src/pong/dtos/GameUpdate.ts index 4804119..d291400 100644 --- a/back/volume/src/pong/dtos/GameUpdate.ts +++ b/back/volume/src/pong/dtos/GameUpdate.ts @@ -1,8 +1,8 @@ -import { type Point } from '../game/utils' +import { type Point } from "../game/utils"; export class GameUpdate { - paddlesPositions!: Point[] - ballSpeed!: Point - ballPosition!: Point - scores!: number[] + paddlesPositions!: Point[]; + ballSpeed!: Point; + ballPosition!: Point; + scores!: number[]; } diff --git a/back/volume/src/pong/dtos/MapDtoValidated.ts b/back/volume/src/pong/dtos/MapDtoValidated.ts index 716e883..bc6115c 100644 --- a/back/volume/src/pong/dtos/MapDtoValidated.ts +++ b/back/volume/src/pong/dtos/MapDtoValidated.ts @@ -1,23 +1,23 @@ -import { Type } from 'class-transformer' +import { Type } from "class-transformer"; import { ArrayMaxSize, IsArray, IsDefined, IsObject, - ValidateNested -} from 'class-validator' -import { PointDtoValidated } from './PointDtoValidated' -import { RectDtoValidated } from './RectDtoValidated' + ValidateNested, +} from "class-validator"; +import { PointDtoValidated } from "./PointDtoValidated"; +import { RectDtoValidated } from "./RectDtoValidated"; export class MapDtoValidated { @IsObject() @IsDefined() @Type(() => PointDtoValidated) - size!: PointDtoValidated + size!: PointDtoValidated; @IsArray() @ArrayMaxSize(5) @ValidateNested({ each: true }) @Type(() => RectDtoValidated) - walls!: RectDtoValidated[] + walls!: RectDtoValidated[]; } diff --git a/back/volume/src/pong/dtos/MatchmakingDto.ts b/back/volume/src/pong/dtos/MatchmakingDto.ts index 580c222..0d8b6ea 100644 --- a/back/volume/src/pong/dtos/MatchmakingDto.ts +++ b/back/volume/src/pong/dtos/MatchmakingDto.ts @@ -1,3 +1,3 @@ export class MatchmakingDto { - matchmaking!: boolean + matchmaking!: boolean; } diff --git a/back/volume/src/pong/dtos/MatchmakingDtoValidated.ts b/back/volume/src/pong/dtos/MatchmakingDtoValidated.ts index d83286c..048befe 100644 --- a/back/volume/src/pong/dtos/MatchmakingDtoValidated.ts +++ b/back/volume/src/pong/dtos/MatchmakingDtoValidated.ts @@ -1,7 +1,7 @@ -import { IsBoolean } from 'class-validator' -import { MatchmakingDto } from './MatchmakingDto' +import { IsBoolean } from "class-validator"; +import { MatchmakingDto } from "./MatchmakingDto"; export class MatchmakingDtoValidated extends MatchmakingDto { @IsBoolean() - matchmaking!: boolean + matchmaking!: boolean; } diff --git a/back/volume/src/pong/dtos/PointDtoValidated.ts b/back/volume/src/pong/dtos/PointDtoValidated.ts index 8a8cbf6..49405ea 100644 --- a/back/volume/src/pong/dtos/PointDtoValidated.ts +++ b/back/volume/src/pong/dtos/PointDtoValidated.ts @@ -1,10 +1,10 @@ -import { IsNumber } from 'class-validator' -import { Point } from '../game/utils' +import { IsNumber } from "class-validator"; +import { Point } from "../game/utils"; export class PointDtoValidated extends Point { @IsNumber() - x!: number + x!: number; @IsNumber() - y!: number + y!: number; } diff --git a/back/volume/src/pong/dtos/RectDtoValidated.ts b/back/volume/src/pong/dtos/RectDtoValidated.ts index d24a682..fec9f6d 100644 --- a/back/volume/src/pong/dtos/RectDtoValidated.ts +++ b/back/volume/src/pong/dtos/RectDtoValidated.ts @@ -1,14 +1,14 @@ -import { Type } from 'class-transformer' -import { ValidateNested } from 'class-validator' -import { Rect } from '../game/utils' -import { PointDtoValidated } from './PointDtoValidated' +import { Type } from "class-transformer"; +import { ValidateNested } from "class-validator"; +import { Rect } from "../game/utils"; +import { PointDtoValidated } from "./PointDtoValidated"; export class RectDtoValidated extends Rect { @ValidateNested() @Type(() => PointDtoValidated) - center!: PointDtoValidated + center!: PointDtoValidated; @ValidateNested() @Type(() => PointDtoValidated) - size!: PointDtoValidated + size!: PointDtoValidated; } diff --git a/back/volume/src/pong/dtos/StringDto.ts b/back/volume/src/pong/dtos/StringDto.ts index 62283b6..1aa81ce 100644 --- a/back/volume/src/pong/dtos/StringDto.ts +++ b/back/volume/src/pong/dtos/StringDto.ts @@ -1,3 +1,3 @@ export class StringDto { - value!: string + value!: string; } diff --git a/back/volume/src/pong/dtos/StringDtoValidated.ts b/back/volume/src/pong/dtos/StringDtoValidated.ts index 0211e22..289655c 100644 --- a/back/volume/src/pong/dtos/StringDtoValidated.ts +++ b/back/volume/src/pong/dtos/StringDtoValidated.ts @@ -1,7 +1,7 @@ -import { IsString } from 'class-validator' -import { StringDto } from './StringDto' +import { IsString } from "class-validator"; +import { StringDto } from "./StringDto"; export class StringDtoValidated extends StringDto { @IsString() - value!: string + value!: string; } diff --git a/back/volume/src/pong/dtos/UserDto.ts b/back/volume/src/pong/dtos/UserDto.ts index 10880a7..27a0bed 100644 --- a/back/volume/src/pong/dtos/UserDto.ts +++ b/back/volume/src/pong/dtos/UserDto.ts @@ -1,12 +1,12 @@ -import { IsString } from 'class-validator' +import { IsString } from "class-validator"; export class UserDto { @IsString() - username!: string + username!: string; @IsString() - avatar!: string + avatar!: string; @IsString() - status!: string + status!: string; } diff --git a/back/volume/src/pong/entity/result.entity.ts b/back/volume/src/pong/entity/result.entity.ts index 037dc33..8c65124 100644 --- a/back/volume/src/pong/entity/result.entity.ts +++ b/back/volume/src/pong/entity/result.entity.ts @@ -3,25 +3,25 @@ import { PrimaryGeneratedColumn, Column, ManyToMany, - CreateDateColumn -} from 'typeorm' + CreateDateColumn, +} from "typeorm"; -import User from 'src/users/entity/user.entity' +import User from "src/users/entity/user.entity"; @Entity() export default class Result { @PrimaryGeneratedColumn() - id: number + id: number; @Column({ default: false }) - ranked: boolean + ranked: boolean; @ManyToMany(() => User, (player: User) => player.results, { cascade: true }) - players: Array // TODO: change to User[] for final version + players: Array; // TODO: change to User[] for final version - @Column('text', { array: true }) - public score: number[] + @Column("text", { array: true }) + public score: number[]; @CreateDateColumn() - date: Date + date: Date; } diff --git a/back/volume/src/pong/game/Ball.ts b/back/volume/src/pong/game/Ball.ts index a93b330..63cbd86 100644 --- a/back/volume/src/pong/game/Ball.ts +++ b/back/volume/src/pong/game/Ball.ts @@ -1,114 +1,114 @@ -import { type Paddle } from './Paddle' -import { type Point, Rect } from './utils' -import { type MapDtoValidated } from '../dtos/MapDtoValidated' +import { type Paddle } from "./Paddle"; +import { type Point, Rect } from "./utils"; +import { type MapDtoValidated } from "../dtos/MapDtoValidated"; import { DEFAULT_BALL_SIZE, GAME_TICKS, DEFAULT_BALL_SPEED_INCREMENT, - DEFAULT_MAX_BALL_SPEED -} from './constants' + DEFAULT_MAX_BALL_SPEED, +} from "./constants"; export class Ball { - rect: Rect - initial_speed: Point - speed: Point - spawn: Point - indexPlayerScored: number - timeoutTime: number + rect: Rect; + initial_speed: Point; + speed: Point; + spawn: Point; + indexPlayerScored: number; + timeoutTime: number; - constructor ( + constructor( spawn: Point, initialSpeed: Point, size: Point = DEFAULT_BALL_SIZE.clone() ) { - this.rect = new Rect(spawn, size) - this.speed = initialSpeed.clone() - this.initial_speed = initialSpeed.clone() - this.spawn = spawn.clone() - this.indexPlayerScored = -1 - this.timeoutTime = 0 + this.rect = new Rect(spawn, size); + this.speed = initialSpeed.clone(); + this.initial_speed = initialSpeed.clone(); + this.spawn = spawn.clone(); + this.indexPlayerScored = -1; + this.timeoutTime = 0; } - getIndexPlayerScored (): number { - return this.indexPlayerScored + getIndexPlayerScored(): number { + return this.indexPlayerScored; } - update (canvasRect: Rect, paddles: Paddle[], map: MapDtoValidated): void { + update(canvasRect: Rect, paddles: Paddle[], map: MapDtoValidated): void { if (!canvasRect.contains_x(this.rect)) { - this.indexPlayerScored = this.playerScored() - this.timeoutTime = 2000 + this.indexPlayerScored = this.playerScored(); + this.timeoutTime = 2000; } else { - this.indexPlayerScored = -1 + this.indexPlayerScored = -1; if (this.timeoutTime <= 0) { - this.move(canvasRect, paddles, map) + this.move(canvasRect, paddles, map); } else { - this.timeoutTime -= 1000 / GAME_TICKS + this.timeoutTime -= 1000 / GAME_TICKS; } } } - move (canvasRect: Rect, paddles: Paddle[], map: MapDtoValidated): void { + move(canvasRect: Rect, paddles: Paddle[], map: MapDtoValidated): void { for (const paddle of paddles) { if (paddle.rect.collides(this.rect)) { if (this.speed.x < 0) { - this.rect.center.x = paddle.rect.center.x + paddle.rect.size.x - } else this.rect.center.x = paddle.rect.center.x - paddle.rect.size.x - this.speed.x = this.speed.x * -1 + this.rect.center.x = paddle.rect.center.x + paddle.rect.size.x; + } else this.rect.center.x = paddle.rect.center.x - paddle.rect.size.x; + this.speed.x = this.speed.x * -1; this.speed.y = ((this.rect.center.y - paddle.rect.center.y) / paddle.rect.size.y) * - 20 - break + 20; + break; } } for (const wall of map.walls) { if (wall.collides(this.rect)) { if (this.speed.x < 0) { - this.rect.center.x = wall.center.x + wall.size.x - } else this.rect.center.x = wall.center.x - wall.size.x - this.speed.x = this.speed.x * -1 + this.rect.center.x = wall.center.x + wall.size.x; + } else this.rect.center.x = wall.center.x - wall.size.x; + this.speed.x = this.speed.x * -1; this.speed.y = - ((this.rect.center.y - wall.center.y) / wall.size.y) * 20 - break + ((this.rect.center.y - wall.center.y) / wall.size.y) * 20; + break; } } - if (!canvasRect.contains_y(this.rect)) this.speed.y = this.speed.y * -1 + if (!canvasRect.contains_y(this.rect)) this.speed.y = this.speed.y * -1; if (this.speed.x > 0 && this.speed.x < DEFAULT_MAX_BALL_SPEED.x) { - this.speed.x += DEFAULT_BALL_SPEED_INCREMENT.x + this.speed.x += DEFAULT_BALL_SPEED_INCREMENT.x; } if (this.speed.x < 0 && this.speed.x > -DEFAULT_MAX_BALL_SPEED.x) { - this.speed.x -= DEFAULT_BALL_SPEED_INCREMENT.x + this.speed.x -= DEFAULT_BALL_SPEED_INCREMENT.x; } if (this.speed.y > 0 && this.speed.y > DEFAULT_MAX_BALL_SPEED.y) { - this.speed.y += DEFAULT_MAX_BALL_SPEED.y + this.speed.y += DEFAULT_MAX_BALL_SPEED.y; } if (this.speed.y < 0 && this.speed.y < -DEFAULT_MAX_BALL_SPEED.y) { - this.speed.y -= DEFAULT_MAX_BALL_SPEED.y + this.speed.y -= DEFAULT_MAX_BALL_SPEED.y; } - this.rect.center.add_inplace(this.speed) + this.rect.center.add_inplace(this.speed); } - playerScored (): number { - let indexPlayerScored: number + playerScored(): number { + let indexPlayerScored: number; if (this.rect.center.x <= this.spawn.x) { - indexPlayerScored = 1 - this.speed.x = this.initial_speed.x + indexPlayerScored = 1; + this.speed.x = this.initial_speed.x; } else { - indexPlayerScored = 0 - this.speed.x = -this.initial_speed.x + indexPlayerScored = 0; + this.speed.x = -this.initial_speed.x; } if (this.speed.y < 0) { - this.speed.y = this.initial_speed.y + this.speed.y = this.initial_speed.y; } else { - this.speed.y = -this.initial_speed.y + this.speed.y = -this.initial_speed.y; } - this.rect.center = this.spawn.clone() + this.rect.center = this.spawn.clone(); - return indexPlayerScored + return indexPlayerScored; } } diff --git a/back/volume/src/pong/game/Game.ts b/back/volume/src/pong/game/Game.ts index 16db48c..6a63861 100644 --- a/back/volume/src/pong/game/Game.ts +++ b/back/volume/src/pong/game/Game.ts @@ -1,33 +1,33 @@ -import { Ball } from './Ball' -import { type Socket } from 'socket.io' -import { Point, Rect } from './utils' -import { Player } from './Player' +import { Ball } from "./Ball"; +import { type Socket } from "socket.io"; +import { Point, Rect } from "./utils"; +import { Player } from "./Player"; import { DEFAULT_BALL_INITIAL_SPEED, DEFAULT_BALL_SIZE, DEFAULT_PADDLE_SIZE, DEFAULT_WIN_SCORE, GAME_EVENTS, - GAME_TICKS -} from './constants' -import { randomUUID } from 'crypto' -import { type MapDtoValidated } from '../dtos/MapDtoValidated' -import { type GameUpdate } from '../dtos/GameUpdate' -import { type GameInfo } from '../dtos/GameInfo' -import { type PongService } from '../pong.service' + GAME_TICKS, +} from "./constants"; +import { randomUUID } from "crypto"; +import { type MapDtoValidated } from "../dtos/MapDtoValidated"; +import { type GameUpdate } from "../dtos/GameUpdate"; +import { type GameInfo } from "../dtos/GameInfo"; +import { type PongService } from "../pong.service"; export class Game { - id: string - timer: NodeJS.Timer | null - map: MapDtoValidated - ball: Ball - players: Player[] = [] - ranked: boolean - initialBallSpeed: Point - waitingForTimeout: boolean - gameStoppedCallback: (name: string) => void + id: string; + timer: NodeJS.Timer | null; + map: MapDtoValidated; + ball: Ball; + players: Player[] = []; + ranked: boolean; + initialBallSpeed: Point; + waitingForTimeout: boolean; + gameStoppedCallback: (name: string) => void; - constructor ( + constructor( sockets: Socket[], uuids: string[], names: string[], @@ -37,24 +37,24 @@ export class Game { ranked: boolean, initialBallSpeed: Point = DEFAULT_BALL_INITIAL_SPEED.clone() ) { - this.id = randomUUID() - this.timer = null - this.ranked = ranked - this.waitingForTimeout = false - this.map = map - this.gameStoppedCallback = gameStoppedCallback - this.initialBallSpeed = initialBallSpeed + this.id = randomUUID(); + this.timer = null; + this.ranked = ranked; + this.waitingForTimeout = false; + this.map = map; + this.gameStoppedCallback = gameStoppedCallback; + this.initialBallSpeed = initialBallSpeed; this.ball = new Ball( new Point(this.map.size.x / 2, this.map.size.y / 2), initialBallSpeed - ) + ); for (let i = 0; i < uuids.length; i++) { - this.addPlayer(sockets[i], uuids[i], names[i]) + this.addPlayer(sockets[i], uuids[i], names[i]); } } - getGameInfo (name: string): GameInfo { - const yourPaddleIndex = this.players.findIndex((p) => p.name === name) + getGameInfo(name: string): GameInfo { + const yourPaddleIndex = this.players.findIndex((p) => p.name === name); return { mapSize: this.map.size, yourPaddleIndex, @@ -63,112 +63,112 @@ export class Game { paddleSize: DEFAULT_PADDLE_SIZE, ballSize: DEFAULT_BALL_SIZE, winScore: DEFAULT_WIN_SCORE, - ranked: this.ranked - } + ranked: this.ranked, + }; } - private addPlayer (socket: Socket, uuid: string, name: string): void { + private addPlayer(socket: Socket, uuid: string, name: string): void { let paddleCoords = new Point( DEFAULT_PADDLE_SIZE.x / 2, this.map.size.y / 2 - ) + ); if (this.players.length === 1) { paddleCoords = new Point( this.map.size.x - DEFAULT_PADDLE_SIZE.x / 2, this.map.size.y / 2 - ) + ); } this.players.push( new Player(socket, uuid, name, paddleCoords, this.map.size) - ) + ); if (this.ranked) { - this.ready(name) + this.ready(name); } } - ready (name: string): void { - const playerIndex: number = this.players.findIndex((p) => p.name === name) + ready(name: string): void { + const playerIndex: number = this.players.findIndex((p) => p.name === name); if (playerIndex !== -1 && !this.players[playerIndex].ready) { - this.players[playerIndex].ready = true - console.log(`${this.players[playerIndex].name} is ready`) + this.players[playerIndex].ready = true; + console.log(`${this.players[playerIndex].name} is ready`); if (this.players.length === 2 && this.players.every((p) => p.ready)) { - this.start() + this.start(); } } } - private start (): void { + private start(): void { if (this.timer === null && this.players.length === 2) { this.ball = new Ball( new Point(this.map.size.x / 2, this.map.size.y / 2), this.initialBallSpeed - ) + ); this.players.forEach((p) => { - void this.pongService.setInGame(p.name) - p.newGame() - }) - this.broadcastGame(GAME_EVENTS.START_GAME) - this.timer = setInterval(this.gameLoop.bind(this), 1000 / GAME_TICKS) - console.log(`Game ${this.id} starting in 3 seconds`) - this.waitingForTimeout = true + void this.pongService.setInGame(p.name); + p.newGame(); + }); + this.broadcastGame(GAME_EVENTS.START_GAME); + this.timer = setInterval(this.gameLoop.bind(this), 1000 / GAME_TICKS); + console.log(`Game ${this.id} starting in 3 seconds`); + this.waitingForTimeout = true; new Promise((resolve) => setTimeout(resolve, 3000)) .then(() => (this.waitingForTimeout = false)) - .catch(() => {}) + .catch(() => {}); } } - stop (): void { + stop(): void { if (this.timer !== null) { - clearInterval(this.timer) + clearInterval(this.timer); } - this.timer = null + this.timer = null; this.pongService .saveResult(this.players, this.ranked, DEFAULT_WIN_SCORE) .then(() => { - this.gameStoppedCallback(this.players[0].name) - this.players = [] + this.gameStoppedCallback(this.players[0].name); + this.players = []; }) .catch(() => { - this.gameStoppedCallback(this.players[0].name) - this.players = [] - }) + this.gameStoppedCallback(this.players[0].name); + this.players = []; + }); } - movePaddle (name: string | undefined, position: Point): void { - const playerIndex: number = this.players.findIndex((p) => p.name === name) + movePaddle(name: string | undefined, position: Point): void { + const playerIndex: number = this.players.findIndex((p) => p.name === name); if (this.timer !== null && playerIndex !== -1) { - this.players[playerIndex].paddle.move(position.y) + this.players[playerIndex].paddle.move(position.y); } } - private broadcastGame (event: string, data?: any): void { + private broadcastGame(event: string, data?: any): void { this.players.forEach((p) => { - p.socket.emit(event, data) - }) + p.socket.emit(event, data); + }); } - private gameLoop (): void { + private gameLoop(): void { if (this.waitingForTimeout) { - return + return; } const canvasRect: Rect = new Rect( new Point(this.map.size.x / 2, this.map.size.y / 2), new Point(this.map.size.x, this.map.size.y) - ) + ); this.ball.update( canvasRect, this.players.map((p) => p.paddle), this.map - ) - const indexPlayerScored: number = this.ball.getIndexPlayerScored() + ); + const indexPlayerScored: number = this.ball.getIndexPlayerScored(); if (indexPlayerScored !== -1) { - this.players[indexPlayerScored].score += 1 + this.players[indexPlayerScored].score += 1; if (this.players[indexPlayerScored].score >= DEFAULT_WIN_SCORE) { - console.log(`${this.players[indexPlayerScored].name} won`) - this.stop() + console.log(`${this.players[indexPlayerScored].name} won`); + this.stop(); } } @@ -176,8 +176,8 @@ export class Game { paddlesPositions: this.players.map((p) => p.paddle.rect.center), ballSpeed: this.ball.speed, ballPosition: this.ball.rect.center, - scores: this.players.map((p) => p.score) - } - this.broadcastGame(GAME_EVENTS.GAME_TICK, data) + scores: this.players.map((p) => p.score), + }; + this.broadcastGame(GAME_EVENTS.GAME_TICK, data); } } diff --git a/back/volume/src/pong/game/Games.ts b/back/volume/src/pong/game/Games.ts index fdd5efc..8cecf94 100644 --- a/back/volume/src/pong/game/Games.ts +++ b/back/volume/src/pong/game/Games.ts @@ -1,33 +1,33 @@ -import { type Socket } from 'socket.io' -import { Game } from './Game' -import { Point } from './utils' -import { type MapDtoValidated as GameMap } from '../dtos/MapDtoValidated' -import { type GameCreationDtoValidated } from '../dtos/GameCreationDtoValidated' -import { type GameInfo } from '../dtos/GameInfo' -import { type PongService } from '../pong.service' +import { type Socket } from "socket.io"; +import { Game } from "./Game"; +import { Point } from "./utils"; +import { type MapDtoValidated as GameMap } from "../dtos/MapDtoValidated"; +import { type GameCreationDtoValidated } from "../dtos/GameCreationDtoValidated"; +import { type GameInfo } from "../dtos/GameInfo"; +import { type PongService } from "../pong.service"; import { DEFAULT_BALL_SIZE, DEFAULT_MAP_SIZE, DEFAULT_PADDLE_SIZE, - DEFAULT_WIN_SCORE -} from './constants' + DEFAULT_WIN_SCORE, +} from "./constants"; export class Games { - constructor (private readonly pongService: PongService) {} - private readonly playerNameToGameIndex = new Map() - private readonly games = new Array() + constructor(private readonly pongService: PongService) {} + private readonly playerNameToGameIndex = new Map(); + private readonly games = new Array(); - newGame ( + newGame( sockets: Socket[], uuids: string[], gameCreationDto: GameCreationDtoValidated, ranked: boolean ): void { - const names: string[] = gameCreationDto.playerNames + const names: string[] = gameCreationDto.playerNames; const map: GameMap = { size: DEFAULT_MAP_SIZE, - walls: gameCreationDto.map.walls - } + walls: gameCreationDto.map.walls, + }; if (!this.isInAGame(names[0]) && !this.isInAGame(names[1])) { this.games.push( new Game( @@ -43,76 +43,76 @@ export class Games { gameCreationDto.initialBallSpeedY ) ) - ) - this.playerNameToGameIndex.set(names[0], this.games.length - 1) - this.playerNameToGameIndex.set(names[1], this.games.length - 1) + ); + this.playerNameToGameIndex.set(names[0], this.games.length - 1); + this.playerNameToGameIndex.set(names[1], this.games.length - 1); console.log( `Created game ${names[0]} vs ${names[1]} (${ this.games[this.games.length - 1].id })` - ) + ); } } - ready (name: string): void { - const game: Game | undefined = this.playerGame(name) + ready(name: string): void { + const game: Game | undefined = this.playerGame(name); if (game !== undefined) { - game.ready(name) + game.ready(name); } } - private deleteGame (name: string): void { - const game: Game | undefined = this.playerGame(name) + private deleteGame(name: string): void { + const game: Game | undefined = this.playerGame(name); if (game !== undefined) { - this.games.splice(this.games.indexOf(game), 1) + this.games.splice(this.games.indexOf(game), 1); game.players.forEach((player) => { - this.playerNameToGameIndex.delete(player.name) - }) - console.log(`Game stopped: ${game.id}`) + this.playerNameToGameIndex.delete(player.name); + }); + console.log(`Game stopped: ${game.id}`); } } - getGameInfo (name: string): GameInfo { - const game: Game | undefined = this.playerGame(name) + getGameInfo(name: string): GameInfo { + const game: Game | undefined = this.playerGame(name); if (game !== undefined) { - return game.getGameInfo(name) + return game.getGameInfo(name); } return { yourPaddleIndex: -2, - gameId: '', + gameId: "", mapSize: new Point(0, 0), walls: [], paddleSize: DEFAULT_PADDLE_SIZE, ballSize: DEFAULT_BALL_SIZE, winScore: DEFAULT_WIN_SCORE, - ranked: false - } + ranked: false, + }; } - movePlayer (name: string | undefined, position: Point): void { - const game: Game | undefined = this.playerGame(name) + movePlayer(name: string | undefined, position: Point): void { + const game: Game | undefined = this.playerGame(name); if (game !== undefined) { - game.movePaddle(name, position) + game.movePaddle(name, position); } } - isInAGame (name: string | undefined): boolean { - if (name === undefined) return false - return this.playerNameToGameIndex.get(name) !== undefined + isInAGame(name: string | undefined): boolean { + if (name === undefined) return false; + return this.playerNameToGameIndex.get(name) !== undefined; } - playerGame (name: string | undefined): Game | undefined { + playerGame(name: string | undefined): Game | undefined { const game: Game | undefined = this.games.find((game) => game.players.some((player) => player.name === name) - ) - return game + ); + return game; } - async leaveGame (name: string): Promise { - const game: Game | undefined = this.playerGame(name) + async leaveGame(name: string): Promise { + const game: Game | undefined = this.playerGame(name); if (game !== undefined && !game.ranked) { - game.stop() - this.deleteGame(name) + game.stop(); + this.deleteGame(name); } } } diff --git a/back/volume/src/pong/game/MatchmakingQueue.ts b/back/volume/src/pong/game/MatchmakingQueue.ts index a475dc6..2918bec 100644 --- a/back/volume/src/pong/game/MatchmakingQueue.ts +++ b/back/volume/src/pong/game/MatchmakingQueue.ts @@ -1,64 +1,64 @@ -import { type Socket } from 'socket.io' -import { type GameCreationDtoValidated } from '../dtos/GameCreationDtoValidated' -import { DEFAULT_BALL_INITIAL_SPEED, DEFAULT_MAP_SIZE } from './constants' -import { type Games } from './Games' +import { type Socket } from "socket.io"; +import { type GameCreationDtoValidated } from "../dtos/GameCreationDtoValidated"; +import { DEFAULT_BALL_INITIAL_SPEED, DEFAULT_MAP_SIZE } from "./constants"; +import { type Games } from "./Games"; export class MatchmakingQueue { - games: Games - queue: Array<{ name: string, socket: Socket, uuid: string }> + games: Games; + queue: Array<{ name: string; socket: Socket; uuid: string }>; - constructor (games: Games) { - this.games = games - this.queue = [] + constructor(games: Games) { + this.games = games; + this.queue = []; } - addPlayer (name: string, socket: Socket, uuid: string): void { + addPlayer(name: string, socket: Socket, uuid: string): void { if (!this.isInQueue(name)) { - console.log('Adding player to queue: ', name) - this.queue.push({ name, socket, uuid }) + console.log("Adding player to queue: ", name); + this.queue.push({ name, socket, uuid }); if (this.canCreateGame()) { - this.createGame() + this.createGame(); } } } - removePlayer (name: string): void { + removePlayer(name: string): void { if (this.isInQueue(name)) { - console.log('Removing player from queue: ', name) - this.queue = this.queue.filter((player) => player.name !== name) + console.log("Removing player from queue: ", name); + this.queue = this.queue.filter((player) => player.name !== name); } } - isInQueue (name: string): boolean { - return this.queue.some((player) => player.name === name) + isInQueue(name: string): boolean { + return this.queue.some((player) => player.name === name); } - canCreateGame (): boolean { - return this.queue.length >= 2 + canCreateGame(): boolean { + return this.queue.length >= 2; } - createGame (): void { - const player1 = this.queue.shift() - const player2 = this.queue.shift() + createGame(): void { + const player1 = this.queue.shift(); + const player2 = this.queue.shift(); if (player1 === undefined || player2 === undefined) { - return + return; } const gameCreationDto: GameCreationDtoValidated = { playerNames: [player1.name, player2.name], map: { size: DEFAULT_MAP_SIZE, - walls: [] + walls: [], }, initialBallSpeedX: DEFAULT_BALL_INITIAL_SPEED.x, - initialBallSpeedY: DEFAULT_BALL_INITIAL_SPEED.y - } - const ranked = true + initialBallSpeedY: DEFAULT_BALL_INITIAL_SPEED.y, + }; + const ranked = true; this.games.newGame( [player1.socket, player2.socket], [player1.uuid, player2.uuid], gameCreationDto, ranked - ) + ); } } diff --git a/back/volume/src/pong/game/Paddle.ts b/back/volume/src/pong/game/Paddle.ts index 324a819..cfb38b6 100644 --- a/back/volume/src/pong/game/Paddle.ts +++ b/back/volume/src/pong/game/Paddle.ts @@ -1,32 +1,32 @@ -import { DEFAULT_PADDLE_SIZE } from './constants' -import { type Point, Rect } from './utils' +import { DEFAULT_PADDLE_SIZE } from "./constants"; +import { type Point, Rect } from "./utils"; export class Paddle { - rect: Rect - color: string | CanvasGradient | CanvasPattern = 'white' - mapSize: Point + rect: Rect; + color: string | CanvasGradient | CanvasPattern = "white"; + mapSize: Point; - constructor ( + constructor( spawn: Point, gameSize: Point, size: Point = DEFAULT_PADDLE_SIZE ) { - this.rect = new Rect(spawn, size) - this.mapSize = gameSize + this.rect = new Rect(spawn, size); + this.mapSize = gameSize; } - draw (context: CanvasRenderingContext2D): void { - this.rect.draw(context, this.color) + draw(context: CanvasRenderingContext2D): void { + this.rect.draw(context, this.color); } - move (newY: number): void { - const offset: number = this.rect.size.y / 2 + move(newY: number): void { + const offset: number = this.rect.size.y / 2; if (newY - offset < 0) { - this.rect.center.y = offset + this.rect.center.y = offset; } else if (newY + offset > this.mapSize.y) { - this.rect.center.y = this.mapSize.y - offset + this.rect.center.y = this.mapSize.y - offset; } else { - this.rect.center.y = newY + this.rect.center.y = newY; } } } diff --git a/back/volume/src/pong/game/Player.ts b/back/volume/src/pong/game/Player.ts index ee9b6da..cbcad17 100644 --- a/back/volume/src/pong/game/Player.ts +++ b/back/volume/src/pong/game/Player.ts @@ -1,36 +1,36 @@ -import { type Socket } from 'socket.io' -import { Paddle } from './Paddle' -import { type Point } from './utils' +import { type Socket } from "socket.io"; +import { Paddle } from "./Paddle"; +import { type Point } from "./utils"; export class Player { - socket: Socket - uuid: string - name: string - ready: boolean - paddle: Paddle - paddleCoords: Point - mapSize: Point - score: number + socket: Socket; + uuid: string; + name: string; + ready: boolean; + paddle: Paddle; + paddleCoords: Point; + mapSize: Point; + score: number; - constructor ( + constructor( socket: Socket, uuid: string, name: string, paddleCoords: Point, mapSize: Point ) { - this.socket = socket - this.uuid = uuid - this.name = name - this.ready = false - this.paddle = new Paddle(paddleCoords, mapSize) - this.paddleCoords = paddleCoords - this.mapSize = mapSize - this.score = 0 + this.socket = socket; + this.uuid = uuid; + this.name = name; + this.ready = false; + this.paddle = new Paddle(paddleCoords, mapSize); + this.paddleCoords = paddleCoords; + this.mapSize = mapSize; + this.score = 0; } - newGame (): void { - this.score = 0 - this.paddle = new Paddle(this.paddleCoords, this.mapSize) + newGame(): void { + this.score = 0; + this.paddle = new Paddle(this.paddleCoords, this.mapSize); } } diff --git a/back/volume/src/pong/game/constants.ts b/back/volume/src/pong/game/constants.ts index 4516899..867dd03 100644 --- a/back/volume/src/pong/game/constants.ts +++ b/back/volume/src/pong/game/constants.ts @@ -1,22 +1,22 @@ -import { Point } from './utils' +import { Point } from "./utils"; export const GAME_EVENTS = { - START_GAME: 'START_GAME', - READY: 'READY', - GAME_TICK: 'GAME_TICK', - PLAYER_MOVE: 'PLAYER_MOVE', - GET_GAME_INFO: 'GET_GAME_INFO', - CREATE_GAME: 'CREATE_GAME', - REGISTER_PLAYER: 'REGISTER_PLAYER', - MATCHMAKING: 'MATCHMAKING', - LEAVE_GAME: 'LEAVE_GAME' -} + START_GAME: "START_GAME", + READY: "READY", + GAME_TICK: "GAME_TICK", + PLAYER_MOVE: "PLAYER_MOVE", + GET_GAME_INFO: "GET_GAME_INFO", + CREATE_GAME: "CREATE_GAME", + REGISTER_PLAYER: "REGISTER_PLAYER", + MATCHMAKING: "MATCHMAKING", + LEAVE_GAME: "LEAVE_GAME", +}; -export const DEFAULT_MAP_SIZE = new Point(500, 400) -export const DEFAULT_PADDLE_SIZE = new Point(30, 50) -export const DEFAULT_BALL_SIZE = new Point(10, 10) -export const DEFAULT_BALL_INITIAL_SPEED = new Point(10, 2) -export const DEFAULT_MAX_BALL_SPEED = new Point(30, 20) -export const DEFAULT_BALL_SPEED_INCREMENT = new Point(0.05, 0) -export const DEFAULT_WIN_SCORE = 5 -export const GAME_TICKS = 30 +export const DEFAULT_MAP_SIZE = new Point(500, 400); +export const DEFAULT_PADDLE_SIZE = new Point(30, 50); +export const DEFAULT_BALL_SIZE = new Point(10, 10); +export const DEFAULT_BALL_INITIAL_SPEED = new Point(10, 2); +export const DEFAULT_MAX_BALL_SPEED = new Point(30, 20); +export const DEFAULT_BALL_SPEED_INCREMENT = new Point(0.05, 0); +export const DEFAULT_WIN_SCORE = 5; +export const GAME_TICKS = 30; diff --git a/back/volume/src/pong/game/utils.ts b/back/volume/src/pong/game/utils.ts index d26ea43..081c9c5 100644 --- a/back/volume/src/pong/game/utils.ts +++ b/back/volume/src/pong/game/utils.ts @@ -1,83 +1,83 @@ export class Point { - x: number - y: number + x: number; + y: number; - constructor (x: number, y: number) { - this.x = x - this.y = y + constructor(x: number, y: number) { + this.x = x; + this.y = y; } // Returns a new point - add (other: Point): Point { - return new Point(this.x + other.x, this.y + other.y) + add(other: Point): Point { + return new Point(this.x + other.x, this.y + other.y); } // Modifies `this` point - add_inplace (other: Point): void { - this.x += other.x - this.y += other.y + add_inplace(other: Point): void { + this.x += other.x; + this.y += other.y; } - clone (): Point { - return new Point(this.x, this.y) + clone(): Point { + return new Point(this.x, this.y); } } export class Rect { - center: Point - size: Point + center: Point; + size: Point; - constructor (center: Point, size: Point) { - this.center = center - this.size = size + constructor(center: Point, size: Point) { + this.center = center; + this.size = size; } - draw ( + draw( context: CanvasRenderingContext2D, color: string | CanvasGradient | CanvasPattern ): void { - const offset: Point = new Point(this.size.x / 2, this.size.y / 2) + const offset: Point = new Point(this.size.x / 2, this.size.y / 2); - context.fillStyle = color + context.fillStyle = color; context.fillRect( this.center.x - offset.x, this.center.y - offset.y, this.size.x, this.size.y - ) + ); } // True if `this` rect contains `other` rect in the x-axis - contains_x (other: Rect): boolean { - const offset: number = this.size.x / 2 - const offsetOther: number = other.size.x / 2 + contains_x(other: Rect): boolean { + const offset: number = this.size.x / 2; + const offsetOther: number = other.size.x / 2; if ( this.center.x - offset <= other.center.x - offsetOther && this.center.x + offset >= other.center.x + offsetOther ) { - return true + return true; } - return false + return false; } // True if `this` rect contains `other` rect in the y-axis - contains_y (other: Rect): boolean { - const offset: number = this.size.y / 2 - const offsetOther: number = other.size.y / 2 + contains_y(other: Rect): boolean { + const offset: number = this.size.y / 2; + const offsetOther: number = other.size.y / 2; if ( this.center.y - offset <= other.center.y - offsetOther && this.center.y + offset >= other.center.y + offsetOther ) { - return true + return true; } - return false + return false; } - collides (other: Rect): boolean { - const offset: Point = new Point(this.size.x / 2, this.size.y / 2) - const offsetOther: Point = new Point(other.size.x / 2, other.size.y / 2) + collides(other: Rect): boolean { + const offset: Point = new Point(this.size.x / 2, this.size.y / 2); + const offsetOther: Point = new Point(other.size.x / 2, other.size.y / 2); if ( this.center.x - offset.x < other.center.x + offsetOther.x && @@ -85,8 +85,8 @@ export class Rect { this.center.y - offset.y < other.center.y + offsetOther.y && this.center.y + offset.y > other.center.y - offsetOther.y ) { - return true + return true; } - return false + return false; } } diff --git a/back/volume/src/pong/pong.controller.ts b/back/volume/src/pong/pong.controller.ts index 809880d..5016cac 100644 --- a/back/volume/src/pong/pong.controller.ts +++ b/back/volume/src/pong/pong.controller.ts @@ -3,31 +3,31 @@ import { Get, Param, ParseIntPipe, - UseGuards -} from '@nestjs/common' -import { Paginate, type Paginated, PaginateQuery } from 'nestjs-paginate' -import { AuthenticatedGuard } from 'src/auth/42-auth.guard' -import type Result from './entity/result.entity' -import { PongService } from './pong.service' + UseGuards, +} from "@nestjs/common"; +import { Paginate, type Paginated, PaginateQuery } from "nestjs-paginate"; +import { AuthenticatedGuard } from "src/auth/42-auth.guard"; +import type Result from "./entity/result.entity"; +import { PongService } from "./pong.service"; -@Controller('results') +@Controller("results") export class PongController { - constructor (private readonly pongService: PongService) {} + constructor(private readonly pongService: PongService) {} - @Get('global') + @Get("global") @UseGuards(AuthenticatedGuard) - async getGlobalHistory ( + async getGlobalHistory( @Paginate() query: PaginateQuery ): Promise> { - return await this.pongService.getHistory(query, 0) + return await this.pongService.getHistory(query, 0); } - @Get(':id') + @Get(":id") @UseGuards(AuthenticatedGuard) - async getHistoryById ( - @Param('id', ParseIntPipe) id: number, - @Paginate() query: PaginateQuery + async getHistoryById( + @Param("id", ParseIntPipe) id: number, + @Paginate() query: PaginateQuery ): Promise> { - return await this.pongService.getHistory(query, id) + return await this.pongService.getHistory(query, id); } } diff --git a/back/volume/src/pong/pong.gateway.spec.ts b/back/volume/src/pong/pong.gateway.spec.ts index f779c23..1283d5c 100644 --- a/back/volume/src/pong/pong.gateway.spec.ts +++ b/back/volume/src/pong/pong.gateway.spec.ts @@ -1,18 +1,18 @@ -import { Test, type TestingModule } from '@nestjs/testing' -import { PongGateway } from './pong.gateway' +import { Test, type TestingModule } from "@nestjs/testing"; +import { PongGateway } from "./pong.gateway"; -describe('PongGateway', () => { - let gateway: PongGateway +describe("PongGateway", () => { + let gateway: PongGateway; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [PongGateway] - }).compile() + providers: [PongGateway], + }).compile(); - gateway = module.get(PongGateway) - }) + gateway = module.get(PongGateway); + }); - it('should be defined', () => { - expect(gateway).toBeDefined() - }) -}) + it("should be defined", () => { + expect(gateway).toBeDefined(); + }); +}); diff --git a/back/volume/src/pong/pong.gateway.ts b/back/volume/src/pong/pong.gateway.ts index f2df12b..1fc0671 100644 --- a/back/volume/src/pong/pong.gateway.ts +++ b/back/volume/src/pong/pong.gateway.ts @@ -1,119 +1,119 @@ -import { UsePipes, ValidationPipe } from '@nestjs/common' -import { Socket } from 'socket.io' +import { UsePipes, ValidationPipe } from "@nestjs/common"; +import { Socket } from "socket.io"; import { ConnectedSocket, MessageBody, type OnGatewayConnection, type OnGatewayDisconnect, SubscribeMessage, - WebSocketGateway -} from '@nestjs/websockets' - -import { Games } from './game/Games' -import { GAME_EVENTS } from './game/constants' -import { GameCreationDtoValidated } from './dtos/GameCreationDtoValidated' -import { type Game } from './game/Game' -import { plainToClass } from 'class-transformer' -import { PointDtoValidated } from './dtos/PointDtoValidated' -import { StringDtoValidated } from './dtos/StringDtoValidated' -import { MatchmakingQueue } from './game/MatchmakingQueue' -import { MatchmakingDtoValidated } from './dtos/MatchmakingDtoValidated' -import { PongService } from './pong.service' -import { UsersService } from 'src/users/users.service' + WebSocketGateway, +} from "@nestjs/websockets"; + +import { Games } from "./game/Games"; +import { GAME_EVENTS } from "./game/constants"; +import { GameCreationDtoValidated } from "./dtos/GameCreationDtoValidated"; +import { type Game } from "./game/Game"; +import { plainToClass } from "class-transformer"; +import { PointDtoValidated } from "./dtos/PointDtoValidated"; +import { StringDtoValidated } from "./dtos/StringDtoValidated"; +import { MatchmakingQueue } from "./game/MatchmakingQueue"; +import { MatchmakingDtoValidated } from "./dtos/MatchmakingDtoValidated"; +import { PongService } from "./pong.service"; +import { UsersService } from "src/users/users.service"; @WebSocketGateway({ - cors: { origin: /^(http|ws):\/\/localhost(:\d+)?$/ } + cors: { origin: /^(http|ws):\/\/localhost(:\d+)?$/ }, }) export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect { - constructor ( + constructor( private readonly pongService: PongService, private readonly usersService: UsersService ) {} - private readonly games: Games = new Games(this.pongService) - private readonly socketToPlayerName = new Map() - private readonly matchmakingQueue = new MatchmakingQueue(this.games) + private readonly games: Games = new Games(this.pongService); + private readonly socketToPlayerName = new Map(); + private readonly matchmakingQueue = new MatchmakingQueue(this.games); - playerIsRegistered (name: string): boolean { - return Array.from(this.socketToPlayerName.values()).includes(name) + playerIsRegistered(name: string): boolean { + return Array.from(this.socketToPlayerName.values()).includes(name); } - handleConnection (): void {} + handleConnection(): void {} - handleDisconnect ( + handleDisconnect( @ConnectedSocket() - client: Socket + client: Socket ): void { - const name: string | undefined = this.socketToPlayerName.get(client) - const game: Game | undefined = this.games.playerGame(name) + const name: string | undefined = this.socketToPlayerName.get(client); + const game: Game | undefined = this.games.playerGame(name); if (game !== undefined) { - game.stop() + game.stop(); } if (name !== undefined) { - console.log('Disconnected ', this.socketToPlayerName.get(client)) - this.matchmakingQueue.removePlayer(name) - this.socketToPlayerName.delete(client) + console.log("Disconnected ", this.socketToPlayerName.get(client)); + this.matchmakingQueue.removePlayer(name); + this.socketToPlayerName.delete(client); } } @UsePipes(new ValidationPipe({ whitelist: true })) @SubscribeMessage(GAME_EVENTS.REGISTER_PLAYER) - async registerPlayer ( + async registerPlayer( @ConnectedSocket() - client: Socket, - @MessageBody('playerName') playerName: StringDtoValidated, - @MessageBody('socketKey') socketKey: StringDtoValidated - ): Promise<{ event: string, data: boolean }> { - let succeeded: boolean = false - const user = await this.usersService.findUserByName(playerName.value) + client: Socket, + @MessageBody("playerName") playerName: StringDtoValidated, + @MessageBody("socketKey") socketKey: StringDtoValidated + ): Promise<{ event: string; data: boolean }> { + let succeeded: boolean = false; + const user = await this.usersService.findUserByName(playerName.value); if ( user !== null && user.socketKey === socketKey.value && !this.playerIsRegistered(playerName.value) ) { - this.socketToPlayerName.set(client, playerName.value) - succeeded = true - console.log('Registered player', playerName.value) + this.socketToPlayerName.set(client, playerName.value); + succeeded = true; + console.log("Registered player", playerName.value); } else { - console.log('Failed to register player', playerName.value) + console.log("Failed to register player", playerName.value); } - return { event: GAME_EVENTS.REGISTER_PLAYER, data: succeeded } + return { event: GAME_EVENTS.REGISTER_PLAYER, data: succeeded }; } @SubscribeMessage(GAME_EVENTS.GET_GAME_INFO) - getPlayerCount (@ConnectedSocket() client: Socket): void { - const name: string | undefined = this.socketToPlayerName.get(client) + getPlayerCount(@ConnectedSocket() client: Socket): void { + const name: string | undefined = this.socketToPlayerName.get(client); if (name !== undefined) { - client.emit(GAME_EVENTS.GET_GAME_INFO, this.games.getGameInfo(name)) + client.emit(GAME_EVENTS.GET_GAME_INFO, this.games.getGameInfo(name)); } } @UsePipes(new ValidationPipe({ whitelist: true })) @SubscribeMessage(GAME_EVENTS.PLAYER_MOVE) - movePlayer ( + movePlayer( @ConnectedSocket() - client: Socket, - @MessageBody() position: PointDtoValidated + client: Socket, + @MessageBody() position: PointDtoValidated ): void { const realPosition: PointDtoValidated = plainToClass( PointDtoValidated, position - ) - const name: string | undefined = this.socketToPlayerName.get(client) - this.games.movePlayer(name, realPosition) + ); + const name: string | undefined = this.socketToPlayerName.get(client); + this.games.movePlayer(name, realPosition); } @UsePipes(new ValidationPipe({ whitelist: true })) @SubscribeMessage(GAME_EVENTS.CREATE_GAME) - createGame ( + createGame( @ConnectedSocket() - client: Socket, - @MessageBody() gameCreationDto: GameCreationDtoValidated - ): { event: string, data: boolean } { + client: Socket, + @MessageBody() gameCreationDto: GameCreationDtoValidated + ): { event: string; data: boolean } { const realGameCreationDto: GameCreationDtoValidated = plainToClass( GameCreationDtoValidated, gameCreationDto - ) + ); if (this.socketToPlayerName.size >= 2) { const player1Socket: Socket | undefined = Array.from( @@ -122,14 +122,14 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect { (key) => this.socketToPlayerName.get(key) === realGameCreationDto.playerNames[0] - ) + ); const player2Socket: Socket | undefined = Array.from( this.socketToPlayerName.keys() ).find( (key) => this.socketToPlayerName.get(key) === realGameCreationDto.playerNames[1] - ) + ); if ( player1Socket !== undefined && @@ -137,68 +137,68 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect { (client.id === player1Socket.id || client.id === player2Socket.id) && player1Socket.id !== player2Socket.id ) { - this.matchmakingQueue.removePlayer(realGameCreationDto.playerNames[0]) - this.matchmakingQueue.removePlayer(realGameCreationDto.playerNames[1]) + this.matchmakingQueue.removePlayer(realGameCreationDto.playerNames[0]); + this.matchmakingQueue.removePlayer(realGameCreationDto.playerNames[1]); - const ranked = false + const ranked = false; this.games.newGame( [player1Socket, player2Socket], [player1Socket.id, player2Socket.id], realGameCreationDto, ranked - ) - return { event: GAME_EVENTS.CREATE_GAME, data: true } + ); + return { event: GAME_EVENTS.CREATE_GAME, data: true }; } } - return { event: GAME_EVENTS.CREATE_GAME, data: false } + return { event: GAME_EVENTS.CREATE_GAME, data: false }; } @SubscribeMessage(GAME_EVENTS.READY) - ready ( + ready( @ConnectedSocket() - client: Socket - ): { event: string, data: boolean } { - let succeeded: boolean = false - const name: string | undefined = this.socketToPlayerName.get(client) + client: Socket + ): { event: string; data: boolean } { + let succeeded: boolean = false; + const name: string | undefined = this.socketToPlayerName.get(client); if (name !== undefined) { - this.games.ready(name) - succeeded = true + this.games.ready(name); + succeeded = true; } - return { event: GAME_EVENTS.READY, data: succeeded } + return { event: GAME_EVENTS.READY, data: succeeded }; } @UsePipes(new ValidationPipe({ whitelist: true })) @SubscribeMessage(GAME_EVENTS.MATCHMAKING) - updateMatchmaking ( + updateMatchmaking( @ConnectedSocket() - client: Socket, - @MessageBody() matchmakingUpdateData: MatchmakingDtoValidated - ): { event: string, data: MatchmakingDtoValidated } { - let matchmaking: boolean = false - const name: string | undefined = this.socketToPlayerName.get(client) + client: Socket, + @MessageBody() matchmakingUpdateData: MatchmakingDtoValidated + ): { event: string; data: MatchmakingDtoValidated } { + let matchmaking: boolean = false; + const name: string | undefined = this.socketToPlayerName.get(client); if (name !== undefined) { if (matchmakingUpdateData.matchmaking && !this.games.isInAGame(name)) { - this.matchmakingQueue.addPlayer(name, client, client.id) + this.matchmakingQueue.addPlayer(name, client, client.id); } else { - this.matchmakingQueue.removePlayer(name) + this.matchmakingQueue.removePlayer(name); } - matchmaking = this.matchmakingQueue.isInQueue(name) + matchmaking = this.matchmakingQueue.isInQueue(name); } return { event: GAME_EVENTS.MATCHMAKING, - data: { matchmaking } - } + data: { matchmaking }, + }; } @UsePipes(new ValidationPipe({ whitelist: true })) @SubscribeMessage(GAME_EVENTS.LEAVE_GAME) - leaveGame ( + leaveGame( @ConnectedSocket() - client: Socket + client: Socket ): void { - const name: string | undefined = this.socketToPlayerName.get(client) + const name: string | undefined = this.socketToPlayerName.get(client); if (name !== undefined) { - void this.games.leaveGame(name) + void this.games.leaveGame(name); } } } diff --git a/back/volume/src/pong/pong.module.ts b/back/volume/src/pong/pong.module.ts index df862a7..72b257d 100644 --- a/back/volume/src/pong/pong.module.ts +++ b/back/volume/src/pong/pong.module.ts @@ -1,15 +1,15 @@ -import { forwardRef, Module } from '@nestjs/common' -import { PongGateway } from './pong.gateway' -import Result from './entity/result.entity' -import { TypeOrmModule } from '@nestjs/typeorm' -import { PongService } from './pong.service' -import { UsersModule } from 'src/users/users.module' -import { PongController } from './pong.controller' +import { forwardRef, Module } from "@nestjs/common"; +import { PongGateway } from "./pong.gateway"; +import Result from "./entity/result.entity"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { PongService } from "./pong.service"; +import { UsersModule } from "src/users/users.module"; +import { PongController } from "./pong.controller"; @Module({ imports: [forwardRef(() => UsersModule), TypeOrmModule.forFeature([Result])], providers: [PongGateway, PongService], controllers: [PongController], - exports: [PongService] + exports: [PongService], }) export class PongModule {} diff --git a/back/volume/src/pong/pong.service.ts b/back/volume/src/pong/pong.service.ts index d64b78a..0160551 100644 --- a/back/volume/src/pong/pong.service.ts +++ b/back/volume/src/pong/pong.service.ts @@ -1,89 +1,89 @@ -import { Injectable } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { Repository } from 'typeorm' -import { UsersService } from 'src/users/users.service' -import Result from './entity/result.entity' -import type User from 'src/users/entity/user.entity' -import { type Player } from './game/Player' -import { type PaginateQuery, paginate, type Paginated } from 'nestjs-paginate' +import { Injectable } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; +import { UsersService } from "src/users/users.service"; +import Result from "./entity/result.entity"; +import type User from "src/users/entity/user.entity"; +import { type Player } from "./game/Player"; +import { type PaginateQuery, paginate, type Paginated } from "nestjs-paginate"; @Injectable() export class PongService { - constructor ( + constructor( @InjectRepository(Result) private readonly resultsRepository: Repository, private readonly usersService: UsersService ) {} - async updateStats ( + async updateStats( player: User, i: number, result: Result, maxScore: number ): Promise { - player.matchs++ - if (result.score[i] === maxScore) player.wins++ - else player.looses++ - player.winrate = (100 * player.wins) / player.matchs + player.matchs++; + if (result.score[i] === maxScore) player.wins++; + else player.looses++; + player.winrate = (100 * player.wins) / player.matchs; } - async updatePlayer ( + async updatePlayer( i: number, result: Result, maxScore: number ): Promise { - const player: User | null = result.players[i] - if (player == null) return - if (result.ranked) await this.updateStats(player, i, result, maxScore) - player.results.push(result) - player.status = 'online' - await this.usersService.save(player) + const player: User | null = result.players[i]; + if (player == null) return; + if (result.ranked) await this.updateStats(player, i, result, maxScore); + player.results.push(result); + player.status = "online"; + await this.usersService.save(player); } - async setInGame (playerName: string): Promise { - const player = await this.usersService.findUserByName(playerName) - player.status = 'in-game' - await this.usersService.save(player) + async setInGame(playerName: string): Promise { + const player = await this.usersService.findUserByName(playerName); + player.status = "in-game"; + await this.usersService.save(player); } - async saveResult ( + async saveResult( players: Player[], ranked: boolean, maxScore: number ): Promise { - const result = new Result() - const ply = new Array() - ply.push(await this.usersService.findUserByName(players[0].name)) - ply.push(await this.usersService.findUserByName(players[1].name)) - result.ranked = ranked - result.players = ply - result.score = [players[0].score, players[1].score] - await this.resultsRepository.save(result) - await this.updatePlayer(0, result, maxScore) - await this.updatePlayer(1, result, maxScore) + const result = new Result(); + const ply = new Array(); + ply.push(await this.usersService.findUserByName(players[0].name)); + ply.push(await this.usersService.findUserByName(players[1].name)); + result.ranked = ranked; + result.players = ply; + result.score = [players[0].score, players[1].score]; + await this.resultsRepository.save(result); + await this.updatePlayer(0, result, maxScore); + await this.updatePlayer(1, result, maxScore); } - async getHistory ( + async getHistory( query: PaginateQuery, ftId: number ): Promise> { - let queryBuilder + let queryBuilder; if (ftId !== 0) { queryBuilder = this.resultsRepository - .createQueryBuilder('result') - .innerJoin('result.players', 'player', 'player.ftId = :ftId', { ftId }) + .createQueryBuilder("result") + .innerJoin("result.players", "player", "player.ftId = :ftId", { ftId }); } else { queryBuilder = this.resultsRepository - .createQueryBuilder('result') - .where('result.ranked = :ranked', { ranked: true }) + .createQueryBuilder("result") + .where("result.ranked = :ranked", { ranked: true }); } return await paginate(query, queryBuilder, { - nullSort: 'last', - relations: ['players'], - defaultSortBy: [['date', 'DESC']], - sortableColumns: ['date'], - maxLimit: 10 - }) + nullSort: "last", + relations: ["players"], + defaultSortBy: [["date", "DESC"]], + sortableColumns: ["date"], + maxLimit: 10, + }); } } diff --git a/back/volume/src/pong/pong.spec.ts b/back/volume/src/pong/pong.spec.ts index b11fd5b..522c941 100644 --- a/back/volume/src/pong/pong.spec.ts +++ b/back/volume/src/pong/pong.spec.ts @@ -1,18 +1,18 @@ -import { Test, type TestingModule } from '@nestjs/testing' -import { Games } from './game/Games' +import { Test, type TestingModule } from "@nestjs/testing"; +import { Games } from "./game/Games"; -describe('Pong', () => { - let provider: Games +describe("Pong", () => { + let provider: Games; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [Games] - }).compile() + providers: [Games], + }).compile(); - provider = module.get(Games) - }) + provider = module.get(Games); + }); - it('should be defined', () => { - expect(provider).toBeDefined() - }) -}) + it("should be defined", () => { + expect(provider).toBeDefined(); + }); +}); diff --git a/back/volume/src/types.d.ts b/back/volume/src/types.d.ts index 48e3dde..65f3ee9 100644 --- a/back/volume/src/types.d.ts +++ b/back/volume/src/types.d.ts @@ -1,8 +1,8 @@ -declare module 'passport-42' { - export type Profile = any - export type VerifyCallback = any +declare module "passport-42" { + export type Profile = any; + export type VerifyCallback = any; export class Strategy { - constructor (options: any, verify: any) - authenticate (req: any, options: any): any + constructor(options: any, verify: any); + authenticate(req: any, options: any): any; } } diff --git a/back/volume/src/users/dto/user.dto.ts b/back/volume/src/users/dto/user.dto.ts index fe60681..a521ce8 100644 --- a/back/volume/src/users/dto/user.dto.ts +++ b/back/volume/src/users/dto/user.dto.ts @@ -1,31 +1,31 @@ -import { IsString, IsNotEmpty, IsPositive, IsOptional } from 'class-validator' +import { IsString, IsNotEmpty, IsPositive, IsOptional } from "class-validator"; -import { ApiProperty } from '@nestjs/swagger' -import { Express } from 'express' +import { ApiProperty } from "@nestjs/swagger"; +import { Express } from "express"; export class UserDto { @IsPositive() @IsOptional() - readonly ftId: number + readonly ftId: number; @IsString() @IsNotEmpty() - readonly username: string + readonly username: string; @IsOptional() - readonly status: string + readonly status: string; @IsOptional() - readonly avatar: string + readonly avatar: string; @IsOptional() - readonly authToken: string + readonly authToken: string; @IsOptional() - readonly isVerified: boolean + readonly isVerified: boolean; } export class AvatarUploadDto { - @ApiProperty({ type: 'string', format: 'binary' }) - file: Express.Multer.File + @ApiProperty({ type: "string", format: "binary" }) + file: Express.Multer.File; } diff --git a/back/volume/src/users/entity/user.entity.ts b/back/volume/src/users/entity/user.entity.ts index a52ce7f..dfffb30 100644 --- a/back/volume/src/users/entity/user.entity.ts +++ b/back/volume/src/users/entity/user.entity.ts @@ -4,78 +4,78 @@ import { Column, OneToMany, ManyToMany, - JoinTable -} from 'typeorm' + JoinTable, +} from "typeorm"; -import Message from 'src/chat/entity/message.entity' -import Channel from 'src/chat/entity/channel.entity' -import Result from 'src/pong/entity/result.entity' +import Message from "src/chat/entity/message.entity"; +import Channel from "src/chat/entity/channel.entity"; +import Result from "src/pong/entity/result.entity"; @Entity() export class User { @PrimaryGeneratedColumn() - id: number + id: number; - @Column({ type: 'bigint', default: Date.now() }) - lastAccess: number + @Column({ type: "bigint", default: Date.now() }) + lastAccess: number; @Column({ unique: true }) - ftId: number + ftId: number; @Column({ unique: true, nullable: true }) - email: string + email: string; @Column({ select: false, nullable: true }) - authToken: string + authToken: string; @Column({ default: false }) - twoFA: boolean + twoFA: boolean; @Column({ default: false, nullable: true }) - isVerified: boolean + isVerified: boolean; - @Column('uuid', { unique: true }) - socketKey: string + @Column("uuid", { unique: true }) + socketKey: string; @Column({ unique: true }) - username: string + username: string; - @Column({ default: 'online' }) - status: string + @Column({ default: "online" }) + status: string; - @Column({ name: 'avatar' }) - avatar: string + @Column({ name: "avatar" }) + avatar: string; @Column({ default: 0 }) - wins: number + wins: number; @Column({ default: 0 }) - looses: number + looses: number; @Column({ default: 0 }) - matchs: number + matchs: number; @Column({ default: 0 }) - rank: number + rank: number; - @Column({ default: 0, type: 'double precision' }) - winrate: number + @Column({ default: 0, type: "double precision" }) + winrate: number; @ManyToMany(() => Result, (result: Result) => result.players) @JoinTable() - results: Result[] + results: Result[]; @ManyToMany(() => User) @JoinTable() - blocked: User[] + blocked: User[]; @ManyToMany(() => User) @JoinTable() - followers: User[] + followers: User[]; @ManyToMany(() => User) @JoinTable() - friends: User[] + friends: User[]; } -export default User +export default User; diff --git a/back/volume/src/users/users.controller.ts b/back/volume/src/users/users.controller.ts index 005a298..f4c34ab 100644 --- a/back/volume/src/users/users.controller.ts +++ b/back/volume/src/users/users.controller.ts @@ -11,31 +11,29 @@ import { Res, StreamableFile, BadRequestException, - Redirect -} from '@nestjs/common' + Redirect, +} from "@nestjs/common"; -import { FileInterceptor } from '@nestjs/platform-express' -import { diskStorage } from 'multer' +import { FileInterceptor } from "@nestjs/platform-express"; +import { diskStorage } from "multer"; -import { type User } from './entity/user.entity' -import { UsersService } from './users.service' -import { UserDto, AvatarUploadDto } from './dto/user.dto' -import { PongService } from 'src/pong/pong.service' +import { type User } from "./entity/user.entity"; +import { UsersService } from "./users.service"; +import { UserDto, AvatarUploadDto } from "./dto/user.dto"; +import { PongService } from "src/pong/pong.service"; -import { AuthenticatedGuard } from 'src/auth/42-auth.guard' -import { Profile42 } from 'src/auth/42.decorator' -import { Profile } from 'passport-42' +import { AuthenticatedGuard } from "src/auth/42-auth.guard"; +import { Profile42 } from "src/auth/42.decorator"; +import { Profile } from "passport-42"; -import { ApiBody, ApiConsumes } from '@nestjs/swagger' -import { type Request, Response } from 'express' -import { createReadStream } from 'fs' -import { join } from 'path' +import { ApiBody, ApiConsumes } from "@nestjs/swagger"; +import { type Request, Response } from "express"; +import { createReadStream } from "fs"; +import { join } from "path"; @Controller("users") export class UsersController { - constructor ( - private readonly usersService: UsersService, - ) {} + constructor(private readonly usersService: UsersService) {} @UseGuards(AuthenticatedGuard) @Post("block/:id") @@ -60,145 +58,151 @@ export class UsersController { return await this.usersService.findUsers() } - @Get('online') - async getOnlineUsers (): Promise { - return await this.usersService.findOnlineUsers() + @Get("online") + async getOnlineUsers(): Promise { + return await this.usersService.findOnlineUsers(); } - @Get('friends') + @Get("friends") @UseGuards(AuthenticatedGuard) - async getFriends (@Profile42() profile: Profile): Promise { - return await this.usersService.getFriends(profile.id) + async getFriends(@Profile42() profile: Profile): Promise { + return await this.usersService.getFriends(profile.id); } - @Get('invits') + @Get("invits") @UseGuards(AuthenticatedGuard) - async getInvits (@Profile42() profile: Profile): Promise { - return await this.usersService.getInvits(profile.id) + async getInvits(@Profile42() profile: Profile): Promise { + return await this.usersService.getInvits(profile.id); } - @Get('leaderboard') + @Get("leaderboard") @UseGuards(AuthenticatedGuard) - async getLeaderboard (): Promise { - return await this.usersService.getLeaderboard() + async getLeaderboard(): Promise { + return await this.usersService.getLeaderboard(); } - @Post('avatar') + @Post("avatar") @UseGuards(AuthenticatedGuard) - @Redirect('http://localhost') + @Redirect("http://localhost") @UseInterceptors( - FileInterceptor('avatar', { + FileInterceptor("avatar", { storage: diskStorage({ - destination: 'avatars/' + destination: "avatars/", }), fileFilter: (request: Request, file: Express.Multer.File, callback) => { +<<<<<<< HEAD if (!file.mimetype.includes('image')) { callback(null, false) return +======= + if (!file.mimetype.includes("image")) { + callback(null, false); + return; +>>>>>>> ouai c un peu la merde mais bon } - callback(null, true) - } + callback(null, true); + }, }) ) - @ApiConsumes('multipart/form-data') + @ApiConsumes("multipart/form-data") @ApiBody({ - description: 'A new avatar for the user', - type: AvatarUploadDto + description: "A new avatar for the user", + type: AvatarUploadDto, }) - async changeAvatar ( + async changeAvatar( @Profile42() profile: Profile, - @UploadedFile() file: Express.Multer.File + @UploadedFile() file: Express.Multer.File ): Promise { - if (file === undefined) return - await this.usersService.addAvatar(profile.id, file.filename) + if (file === undefined) return; + await this.usersService.addAvatar(profile.id, file.filename); } - @Get('avatar') + @Get("avatar") @UseGuards(AuthenticatedGuard) - async getAvatar ( + async getAvatar( @Profile42() profile: Profile, - @Res({ passthrough: true }) response: Response + @Res({ passthrough: true }) response: Response ): Promise { - return await this.getAvatarById(profile.id, response) + return await this.getAvatarById(profile.id, response); } - @Get(':name/byname') - async getUserByName (@Param('name') username: string): Promise { - const user = await this.usersService.findUserByName(username) - user.socketKey = '' - return user + @Get(":name/byname") + async getUserByName(@Param("name") username: string): Promise { + const user = await this.usersService.findUserByName(username); + user.socketKey = ""; + return user; } - @Get('invit/:username') + @Get("invit/:username") @UseGuards(AuthenticatedGuard) - async invitUser ( - @Profile42() profile: Profile, - @Param('username') username: string + async invitUser( + @Profile42() profile: Profile, + @Param("username") username: string ): Promise { const target: User | null = await this.usersService.findUserByName( username - ) + ); if (target === null) { - throw new BadRequestException(`User ${username} not found.`) + throw new BadRequestException(`User ${username} not found.`); } if (+profile.id === +target.ftId) { - throw new BadRequestException("You can't invite yourself.") + throw new BadRequestException("You can't invite yourself."); } - const ret: string = await this.usersService.invit(profile.id, target.ftId) - if (ret !== 'OK') throw new BadRequestException(ret) + const ret: string = await this.usersService.invit(profile.id, target.ftId); + if (ret !== "OK") throw new BadRequestException(ret); } - @Get(':id/avatar') - async getAvatarById ( - @Param('id', ParseIntPipe) ftId: number, - @Res({ passthrough: true }) response: Response + @Get(":id/avatar") + async getAvatarById( + @Param("id", ParseIntPipe) ftId: number, + @Res({ passthrough: true }) response: Response ): Promise { - const user: User | null = await this.usersService.findUser(ftId) - if (user === null) throw new BadRequestException('User unknown.') - const filename = user.avatar - const stream = createReadStream(join(process.cwd(), 'avatars/' + filename)) + const user: User | null = await this.usersService.findUser(ftId); + if (user === null) throw new BadRequestException("User unknown."); + const filename = user.avatar; + const stream = createReadStream(join(process.cwd(), "avatars/" + filename)); response.set({ - 'Content-Diposition': `inline; filename='${filename}'`, - 'Content-Type': 'image/jpg' - }) - return new StreamableFile(stream) + "Content-Diposition": `inline; filename='${filename}'`, + "Content-Type": "image/jpg", + }); + return new StreamableFile(stream); } - @Get(':id') - async getUserById ( - @Param('id', ParseIntPipe) ftId: number + @Get(":id") + async getUserById( + @Param("id", ParseIntPipe) ftId: number ): Promise { - const user = await this.usersService.findUser(ftId) - if (user == null) throw new BadRequestException('User unknown.') - user.socketKey = '' - return user + const user = await this.usersService.findUser(ftId); + if (user == null) throw new BadRequestException("User unknown."); + user.socketKey = ""; + return user; } - @Post(':id') + @Post(":id") @UseGuards(AuthenticatedGuard) - async createById (@Body() payload: UserDto): Promise { - const user = await this.usersService.findUser(payload.ftId) + async createById(@Body() payload: UserDto): Promise { + const user = await this.usersService.findUser(payload.ftId); if (user != null) { - await this.usersService.update(user, payload) + await this.usersService.update(user, payload); } else { - await this.usersService.create(payload) + await this.usersService.create(payload); } } @Get() @UseGuards(AuthenticatedGuard) - async getUser (@Profile42() profile: Profile): Promise { - return await this.usersService.findUser(profile.id) + async getUser(@Profile42() profile: Profile): Promise { + return await this.usersService.findUser(profile.id); } @Post() @UseGuards(AuthenticatedGuard) - async updateUser ( + async updateUser( @Body() payload: UserDto, - @Profile42() profile: Profile + @Profile42() profile: Profile ): Promise { - const user = await this.usersService.findUser(profile.id) - if (user == null) throw new BadRequestException('User not found.') - return await this.usersService.update(user, payload) + const user = await this.usersService.findUser(profile.id); + if (user == null) throw new BadRequestException("User not found."); + return await this.usersService.update(user, payload); } } diff --git a/back/volume/src/users/users.module.ts b/back/volume/src/users/users.module.ts index 7406029..cd0e3d7 100644 --- a/back/volume/src/users/users.module.ts +++ b/back/volume/src/users/users.module.ts @@ -1,17 +1,15 @@ -import { forwardRef, Module } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' -import { User } from './entity/user.entity' -import { UsersController } from './users.controller' -import { UsersService } from './users.service' -import { PongModule } from 'src/pong/pong.module' -import { ChatModule } from 'src/chat/chat.module' +import { forwardRef, Module } from "@nestjs/common"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { User } from "./entity/user.entity"; +import { UsersController } from "./users.controller"; +import { UsersService } from "./users.service"; +import { PongModule } from "src/pong/pong.module"; +import { ChatModule } from "src/chat/chat.module"; @Module({ - imports: [ - forwardRef(() => PongModule), - TypeOrmModule.forFeature([User])], + imports: [forwardRef(() => PongModule), TypeOrmModule.forFeature([User])], controllers: [UsersController], providers: [UsersService], - exports: [UsersService] + exports: [UsersService], }) export class UsersModule {} diff --git a/back/volume/src/users/users.service.ts b/back/volume/src/users/users.service.ts index f69eec8..5c3e897 100644 --- a/back/volume/src/users/users.service.ts +++ b/back/volume/src/users/users.service.ts @@ -1,182 +1,184 @@ -import { BadRequestException, Catch, Injectable } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { EntityNotFoundError, QueryFailedError, Repository } from 'typeorm' -import { Cron } from '@nestjs/schedule' -import { randomUUID } from 'crypto' +import { BadRequestException, Catch, Injectable } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { EntityNotFoundError, QueryFailedError, Repository } from "typeorm"; +import { Cron } from "@nestjs/schedule"; +import { randomUUID } from "crypto"; -import { type UserDto } from './dto/user.dto' -import type Channel from 'src/chat/entity/channel.entity' -import User from './entity/user.entity' +import { type UserDto } from "./dto/user.dto"; +import type Channel from "src/chat/entity/channel.entity"; +import User from "./entity/user.entity"; @Injectable() @Catch(QueryFailedError, EntityNotFoundError) export class UsersService { - constructor ( - @InjectRepository(User) private readonly usersRepository: Repository, + constructor( + @InjectRepository(User) private readonly usersRepository: Repository ) {} - async save (user: User): Promise { - await this.usersRepository.save(user) + async save(user: User): Promise { + await this.usersRepository.save(user); } - async findUsers (): Promise { - const users = await this.usersRepository.find({}) - users.forEach((usr) => usr.socketKey = '') - return users + async findUsers(): Promise { + const users = await this.usersRepository.find({}); + users.forEach((usr) => (usr.socketKey = "")); + return users; } - async findUserByName (username: string): Promise { + async findUserByName(username: string): Promise { const user = await this.usersRepository.findOne({ where: { username }, - relations: { results: true } - }) - if (user == null) throw new BadRequestException('User not found.') - return user + relations: { results: true }, + }); + if (user == null) throw new BadRequestException("User not found."); + return user; } - @Cron('0 * * * * *') - async updateStatus (): Promise { - const users = await this.usersRepository.find({}) + @Cron("0 * * * * *") + async updateStatus(): Promise { + const users = await this.usersRepository.find({}); users.forEach((usr) => { if (Date.now() - usr.lastAccess > 60000) { - usr.isVerified = false - usr.status = 'offline' - this.usersRepository.save(usr).catch((err) => console.log(err)) + usr.isVerified = false; + usr.status = "offline"; + this.usersRepository.save(usr).catch((err) => { + console.log(err); + }); } - }) + }); this.getLeaderboard(); } - async findUser (ftId: number): Promise { - const user = await this.usersRepository.findOneBy({ ftId }) - if (user == null) return null - user.lastAccess = Date.now() - if (user.status === 'offline') user.status = 'online' - await this.usersRepository.save(user) - return user + async findUser(ftId: number): Promise { + const user = await this.usersRepository.findOneBy({ ftId }); + if (user == null) return null; + user.lastAccess = Date.now(); + if (user.status === "offline") user.status = "online"; + await this.usersRepository.save(user); + return user; } - async findOnlineUsers (): Promise { + async findOnlineUsers(): Promise { const users = await this.usersRepository.find({ - where: { status: 'online' } - }) - users.forEach((usr) => usr.socketKey = '') - return users + where: { status: "online" }, + }); + users.forEach((usr) => (usr.socketKey = "")); + return users; } - async create (userData: UserDto): Promise { + async create(userData: UserDto): Promise { try { - const newUser = this.usersRepository.create(userData) - newUser.socketKey = randomUUID() - return await this.usersRepository.save(newUser) + const newUser = this.usersRepository.create(userData); + newUser.socketKey = randomUUID(); + return await this.usersRepository.save(newUser); } catch (err) { - throw new BadRequestException('User already exists.') + throw new BadRequestException("User already exists."); } } - async findOnlineInChannel (channel: Channel): Promise { + async findOnlineInChannel(channel: Channel): Promise { return await this.usersRepository - .createQueryBuilder('user') - .where('user.channel = :chan', { chan: channel }) - .andWhere('user.status := status)', { status: 'online' }) - .getMany() + .createQueryBuilder("user") + .where("user.channel = :chan", { chan: channel }) + .andWhere("user.status := status)", { status: "online" }) + .getMany(); } - async update (user: User, changes: UserDto): Promise { - this.usersRepository.merge(user, changes) - return await this.usersRepository.save(user) + async update(user: User, changes: UserDto): Promise { + this.usersRepository.merge(user, changes); + return await this.usersRepository.save(user); } - async addAvatar (ftId: number, filename: string): Promise { - await this.usersRepository.update({ ftId }, { avatar: filename }) + async addAvatar(ftId: number, filename: string): Promise { + await this.usersRepository.update({ ftId }, { avatar: filename }); } - async getFriends (ftId: number): Promise { + async getFriends(ftId: number): Promise { const user = await this.usersRepository.findOne({ where: { ftId }, - relations: { friends: true } - }) - if (user == null) throw new BadRequestException('User not found.') - user.friends.forEach((friend) => friend.socketKey = '') - return user.friends + relations: { friends: true }, + }); + if (user == null) throw new BadRequestException("User not found."); + user.friends.forEach((friend) => (friend.socketKey = "")); + return user.friends; } - async getInvits (ftId: number): Promise { + async getInvits(ftId: number): Promise { const user = await this.usersRepository.findOne({ where: { ftId }, relations: { - followers: true - } - }) - if (user == null) throw new BadRequestException('User not found.') - user.followers.forEach((follower) => follower.socketKey = '') - return user.followers + followers: true, + }, + }); + if (user == null) throw new BadRequestException("User not found."); + user.followers.forEach((follower) => (follower.socketKey = "")); + return user.followers; } - async getLeaderboard (): Promise { + async getLeaderboard(): Promise { const leaderboard = await this.usersRepository.find({ order: { - winrate: 'ASC' - } - }) - let ret = leaderboard.filter((user) => user.matchs !== 0) - let r = 0 + winrate: "ASC", + }, + }); + const ret = leaderboard.filter((user) => user.matchs !== 0); + let r = 0; ret.forEach((usr) => { - usr.rank = r++ - this.usersRepository.save(usr) - usr.socketKey = '' - }) - return ret + usr.rank = r++; + this.usersRepository.save(usr); + usr.socketKey = ""; + }); + return ret; } - async invit (ftId: number, targetFtId: number): Promise { + async invit(ftId: number, targetFtId: number): Promise { const user: User | null = await this.usersRepository.findOne({ where: { ftId }, relations: { followers: true, - friends: true - } - }) - if (user === null) throw new BadRequestException('User not found.') + friends: true, + }, + }); + if (user === null) throw new BadRequestException("User not found."); if (user.friends.findIndex((friend) => friend.ftId === targetFtId) !== -1) { - return 'You are already friends.' + return "You are already friends."; } const target: User | null = await this.usersRepository.findOne({ where: { ftId: targetFtId }, relations: { followers: true, - friends: true - } - }) - if (target == null) return 'Target not found.' + friends: true, + }, + }); + if (target == null) return "Target not found."; const id = user.followers.findIndex( (follower) => follower.ftId === targetFtId - ) + ); if ( target.followers.findIndex((follower) => follower.ftId === user.ftId) !== -1 ) { - return 'Invitation already sent.' + return "Invitation already sent."; } else if ( user.followers.findIndex((follower) => follower.ftId === targetFtId) !== -1 ) { - user.friends.push(target) - target.friends.push(user) - user.followers.splice(id, 1) - await this.usersRepository.save(user) - } else target.followers.push(user) - await this.usersRepository.save(target) - return 'OK' + user.friends.push(target); + target.friends.push(user); + user.followers.splice(id, 1); + await this.usersRepository.save(user); + } else target.followers.push(user); + await this.usersRepository.save(target); + return "OK"; } - async findByCode (code: string): Promise { - const user = await this.usersRepository.findOneBy({ authToken: code }) - if (user == null) throw new BadRequestException('User not found') - return user + async findByCode(code: string): Promise { + const user = await this.usersRepository.findOneBy({ authToken: code }); + if (user == null) throw new BadRequestException("User not found"); + return user; } - async turnOnTwoFactorAuthentication (ftId: number): Promise { - await this.usersRepository.update({ ftId }, { twoFA: true }) + async turnOnTwoFactorAuthentication(ftId: number): Promise { + await this.usersRepository.update({ ftId }, { twoFA: true }); } } diff --git a/docker-compose.yml b/docker-compose.yml index d6b48eb..c2e86d3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,23 +34,4 @@ services: - ./postgres:/var/lib/postgresql/data networks: [transcendence] restart: always - env_file: .env - pgadmin: - links: - - postgres:postgres - container_name: pgadmin - image: dpage/pgadmin4 - ports: - - "8081:80" - volumes: - - /data/pgadmin:/root/.pgadmin - environment: - PGADMIN_DEFAULT_EMAIL: 'usr@usr.com' - PGADMIN_DEFAULT_PASSWORD: 'pw' - GUNICORN_ACCESS_LOGFILE: '/dev/null' - PGADMIN_CONFIG_UPGRADE_CHECK_ENABLED: 'False' - depends_on: - - postgres - networks: [transcendence] - logging: - driver: none + env_file: .env \ No newline at end of file diff --git a/front/volume/.gitignore b/front/volume/.gitignore index 047e6a4..9590d78 100644 --- a/front/volume/.gitignore +++ b/front/volume/.gitignore @@ -129,3 +129,4 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* + diff --git a/front/volume/src/App.svelte b/front/volume/src/App.svelte index cd979b8..00a65cf 100644 --- a/front/volume/src/App.svelte +++ b/front/volume/src/App.svelte @@ -182,7 +182,10 @@ on:keydown={() => setAppState(APPSTATE.CHANNELS + "#" + selectedChannel.name)} > - setAppState(APPSTATE.HISTORY_ID)} /> + setAppState(APPSTATE.HISTORY_ID)} + /> {/if} diff --git a/front/volume/src/components/Channels.svelte b/front/volume/src/components/Channels.svelte index cdee9b0..01c210f 100644 --- a/front/volume/src/components/Channels.svelte +++ b/front/volume/src/components/Channels.svelte @@ -8,14 +8,20 @@ } import { onMount } from "svelte"; import { API_URL, store } from "../Auth"; + import { io } from "../socket"; - -
-
-
- {#each chatMessages as message} -

- openProfile(message.author)} - on:keydown={() => openProfile(message.author)} - style="cursor: pointer;" - > - {message.author} - : {message.text} -

- {/each} -
- {#if showProfileMenu} -
+
+
+ { #each chatMessages as message } + < p class="message" > + { #if !blockedUsers.filter((user) => user.username == message.author).length } + < span +class="message-name" +on: click = {() => openProfile(message.author)} +on: keydown = {() => openProfile(message.author)} +style = "cursor: pointer;" + > + { message.author } + < /span>: {message.text} +{ + /if} + < /p> + { + /each} + < /div> + { #if showProfileMenu } +
-
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - - -
  • -
  • -
-
- {/if} -
- - -
- - {#if showChatMembers} -
-
-
    - {#each chatMembers as member} -
  • -

    - {member.username} - - - - - -

    -

    - ----------------------------------------------------------------------------------- -

    -
  • - {/each} -
-
-
- {/if} -
-
+ on: click | stopPropagation + on: keydown | stopPropagation + > +
    +
  • +
  • + < li > +
+ < /div> + { + /if} + < form on: submit | preventDefault={ sendMessage }> + + + < /form> + < button + on: click | stopPropagation={ toggleChatMembers } + on: keydown | stopPropagation > Chat Members < /button + > + { #if showChatMembers } + < div + class="chatMembers" + on: click | stopPropagation + on: keydown | stopPropagation + > +
+
    + { #each chatMembers as member } + < li > +

    + { member.username } + < button on: click = {() => banUser(member.username) + }> ban < /button> + < button on: click = {() => kickUser(member.username) +} + > kick < /button + > +