Walid Bekkal
2 years ago
63 changed files with 1850 additions and 1737 deletions
@ -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" |
@ -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, |
|||
}, |
|||
}; |
|||
|
@ -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<boolean> { |
|||
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<boolean> { |
|||
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<boolean> { |
|||
const req: Request = context.switchToHttp().getRequest() |
|||
return req.isAuthenticated() |
|||
async canActivate(context: ExecutionContext): Promise<boolean> { |
|||
const req: Request = context.switchToHttp().getRequest(); |
|||
return req.isAuthenticated(); |
|||
} |
|||
} |
|||
|
@ -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; |
|||
} |
|||
) |
|||
); |
|||
|
@ -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<string>('FT_OAUTH_CLIENT_ID'), |
|||
clientSecret: configService.get<string>('FT_OAUTH_CLIENT_SECRET'), |
|||
callbackURL: configService.get<string>('FT_OAUTH_CALLBACK_URL'), |
|||
passReqToCallback: true |
|||
}) |
|||
clientID: configService.get<string>("FT_OAUTH_CLIENT_ID"), |
|||
clientSecret: configService.get<string>("FT_OAUTH_CLIENT_SECRET"), |
|||
callbackURL: configService.get<string>("FT_OAUTH_CALLBACK_URL"), |
|||
passReqToCallback: true, |
|||
}); |
|||
} |
|||
|
|||
async validate ( |
|||
async validate( |
|||
request: { session: { accessToken: string } }, |
|||
accessToken: string, |
|||
refreshToken: string, |
|||
profile: Profile, |
|||
cb: VerifyCallback |
|||
): Promise<VerifyCallback> { |
|||
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); |
|||
} |
|||
} |
|||
|
@ -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<void> { |
|||
const { email, username } = user |
|||
async sendConfirmedEmail(user: User): Promise<void> { |
|||
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<void> { |
|||
user.authToken = Math.floor(10000 + Math.random() * 90000).toString() |
|||
await this.usersService.save(user) |
|||
async sendConfirmationEmail(user: User): Promise<void> { |
|||
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<boolean> { |
|||
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<boolean> { |
|||
const user = await this.usersService.findByCode(code); |
|||
user.authToken = ""; |
|||
user.isVerified = true; |
|||
await this.usersService.save(user); |
|||
await this.sendConfirmedEmail(user); |
|||
return true; |
|||
} |
|||
} |
|||
|
@ -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); |
|||
} |
|||
} |
|||
|
@ -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<Channel>, |
|||
private readonly usersService: UsersService |
|||
) {} |
|||
|
|||
async createChannel (channel: CreateChannelDto): Promise<Channel> { |
|||
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<Channel> { |
|||
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<Channel[]> { |
|||
let rooms: Channel[] = [] |
|||
async getChannelsForUser(ftId: number): Promise<Channel[]> { |
|||
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<void> { |
|||
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> { |
|||
channel.owner = user |
|||
return await this.ChannelRepository.save(channel) |
|||
async addUserToChannel(channel: Channel, user: User): Promise<Channel> { |
|||
channel.owner = user; |
|||
return await this.ChannelRepository.save(channel); |
|||
} |
|||
|
|||
async getChannel (id: number): Promise<Channel> { |
|||
const channel = await this.ChannelRepository.findOneBy({ id }) |
|||
if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) } |
|||
return channel |
|||
async getChannel(id: number): Promise<Channel> { |
|||
const channel = await this.ChannelRepository.findOneBy({ id }); |
|||
if (channel == null) { |
|||
throw new NotFoundException(`Channel #${id} not found`); |
|||
} |
|||
return channel; |
|||
} |
|||
|
|||
async getFullChannel (id: number): Promise<Channel> { |
|||
async getFullChannel(id: number): Promise<Channel> { |
|||
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<boolean> { |
|||
async isOwner(id: number, userId: number): Promise<boolean> { |
|||
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<boolean> { |
|||
async isAdmin(id: number, userId: number): Promise<boolean> { |
|||
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<boolean> { |
|||
async isUser(id: number, userId: number): Promise<boolean> { |
|||
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<boolean> { |
|||
async isBanned(id: number, userId: number): Promise<boolean> { |
|||
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<number> { |
|||
async getMuteDuration(id: number, userId: number): Promise<number> { |
|||
const channel = await this.ChannelRepository.findOne({ |
|||
where: { id }, |
|||
relations: { muted: true } |
|||
}) |
|||
if (channel == null) { throw new NotFoundException(`Channel #${id} not found`) } |
|||
|
|||
const mutation: Array<number> | 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]; |
|||
} |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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<number> |
|||
data: number[]; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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[]; |
|||
} |
|||
|
@ -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<void> { |
|||
const logger = new Logger() |
|||
const app = await NestFactory.create<NestExpressApplication>(AppModule) |
|||
async function bootstrap(): Promise<void> { |
|||
const logger = new Logger(); |
|||
const app = await NestFactory.create<NestExpressApplication>(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); |
|||
}); |
|||
|
@ -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; |
|||
} |
|||
|
@ -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[]; |
|||
} |
|||
|
@ -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[]; |
|||
} |
|||
|
@ -1,3 +1,3 @@ |
|||
export class MatchmakingDto { |
|||
matchmaking!: boolean |
|||
matchmaking!: boolean; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -1,3 +1,3 @@ |
|||
export class StringDto { |
|||
value!: string |
|||
value!: string; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
} |
|||
|
@ -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 |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
@ -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; |
|||
} |
|||
} |
|||
} |
|||
|
@ -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); |
|||
} |
|||
} |
|||
|
@ -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; |
|||
|
@ -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>(PongGateway) |
|||
}) |
|||
gateway = module.get<PongGateway>(PongGateway); |
|||
}); |
|||
|
|||
it('should be defined', () => { |
|||
expect(gateway).toBeDefined() |
|||
}) |
|||
}) |
|||
it("should be defined", () => { |
|||
expect(gateway).toBeDefined(); |
|||
}); |
|||
}); |
|||
|
@ -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 {} |
|||
|
@ -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<Result>, |
|||
private readonly usersService: UsersService |
|||
) {} |
|||
|
|||
async updateStats ( |
|||
async updateStats( |
|||
player: User, |
|||
i: number, |
|||
result: Result, |
|||
maxScore: number |
|||
): Promise<void> { |
|||
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<void> { |
|||
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<void> { |
|||
const player = await this.usersService.findUserByName(playerName) |
|||
player.status = 'in-game' |
|||
await this.usersService.save(player) |
|||
async setInGame(playerName: string): Promise<void> { |
|||
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<void> { |
|||
const result = new Result() |
|||
const ply = new Array<User | null>() |
|||
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<User | null>(); |
|||
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<Paginated<Result>> { |
|||
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, |
|||
}); |
|||
} |
|||
} |
|||
|
@ -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>(Games) |
|||
}) |
|||
provider = module.get<Games>(Games); |
|||
}); |
|||
|
|||
it('should be defined', () => { |
|||
expect(provider).toBeDefined() |
|||
}) |
|||
}) |
|||
it("should be defined", () => { |
|||
expect(provider).toBeDefined(); |
|||
}); |
|||
}); |
|||
|
@ -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; |
|||
} |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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 {} |
|||
|
@ -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<User>, |
|||
constructor( |
|||
@InjectRepository(User) private readonly usersRepository: Repository<User> |
|||
) {} |
|||
|
|||
async save (user: User): Promise<void> { |
|||
await this.usersRepository.save(user) |
|||
async save(user: User): Promise<void> { |
|||
await this.usersRepository.save(user); |
|||
} |
|||
|
|||
async findUsers (): Promise<User[]> { |
|||
const users = await this.usersRepository.find({}) |
|||
users.forEach((usr) => usr.socketKey = '') |
|||
return users |
|||
async findUsers(): Promise<User[]> { |
|||
const users = await this.usersRepository.find({}); |
|||
users.forEach((usr) => (usr.socketKey = "")); |
|||
return users; |
|||
} |
|||
|
|||
async findUserByName (username: string): Promise<User> { |
|||
async findUserByName(username: string): Promise<User> { |
|||
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<void> { |
|||
const users = await this.usersRepository.find({}) |
|||
@Cron("0 * * * * *") |
|||
async updateStatus(): Promise<void> { |
|||
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<User | null> { |
|||
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<User | null> { |
|||
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<User[]> { |
|||
async findOnlineUsers(): Promise<User[]> { |
|||
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<User | null> { |
|||
async create(userData: UserDto): Promise<User | null> { |
|||
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<User[]> { |
|||
async findOnlineInChannel(channel: Channel): Promise<User[]> { |
|||
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<User | null> { |
|||
this.usersRepository.merge(user, changes) |
|||
return await this.usersRepository.save(user) |
|||
async update(user: User, changes: UserDto): Promise<User | null> { |
|||
this.usersRepository.merge(user, changes); |
|||
return await this.usersRepository.save(user); |
|||
} |
|||
|
|||
async addAvatar (ftId: number, filename: string): Promise<void> { |
|||
await this.usersRepository.update({ ftId }, { avatar: filename }) |
|||
async addAvatar(ftId: number, filename: string): Promise<void> { |
|||
await this.usersRepository.update({ ftId }, { avatar: filename }); |
|||
} |
|||
|
|||
async getFriends (ftId: number): Promise<User[]> { |
|||
async getFriends(ftId: number): Promise<User[]> { |
|||
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<User[]> { |
|||
async getInvits(ftId: number): Promise<User[]> { |
|||
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<User[]> { |
|||
async getLeaderboard(): Promise<User[]> { |
|||
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<string> { |
|||
async invit(ftId: number, targetFtId: number): Promise<string> { |
|||
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<User> { |
|||
const user = await this.usersRepository.findOneBy({ authToken: code }) |
|||
if (user == null) throw new BadRequestException('User not found') |
|||
return user |
|||
async findByCode(code: string): Promise<User> { |
|||
const user = await this.usersRepository.findOneBy({ authToken: code }); |
|||
if (user == null) throw new BadRequestException("User not found"); |
|||
return user; |
|||
} |
|||
|
|||
async turnOnTwoFactorAuthentication (ftId: number): Promise<void> { |
|||
await this.usersRepository.update({ ftId }, { twoFA: true }) |
|||
async turnOnTwoFactorAuthentication(ftId: number): Promise<void> { |
|||
await this.usersRepository.update({ ftId }, { twoFA: true }); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,5 @@ |
|||
import ioClient from "socket.io-client"; |
|||
|
|||
export const io = ioClient("http://localhost:3001", { |
|||
withCredentials: true, |
|||
}); |
Loading…
Reference in new issue