From faf2fca9dec3583a397d9d954a8fba3574315452 Mon Sep 17 00:00:00 2001 From: Saad eddyne El abdari Date: Tue, 31 Oct 2023 15:50:02 +0100 Subject: [PATCH 1/7] init game --- backend/code/src/app.controller.ts | 7 ++++ backend/code/src/game/game.service.ts | 16 +++++---- backend/code/src/game/game.ts | 35 +++++++++++++++++++ backend/code/src/gateways/gateways.gateway.ts | 8 +++++ 4 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 backend/code/src/game/game.ts diff --git a/backend/code/src/app.controller.ts b/backend/code/src/app.controller.ts index e9983e6..f563d8c 100644 --- a/backend/code/src/app.controller.ts +++ b/backend/code/src/app.controller.ts @@ -40,6 +40,7 @@ export class AppController { + diff --git a/backend/code/src/game/game.service.ts b/backend/code/src/game/game.service.ts index 28a3c4a..7c71e23 100644 --- a/backend/code/src/game/game.service.ts +++ b/backend/code/src/game/game.service.ts @@ -1,12 +1,15 @@ import { Injectable } from '@nestjs/common'; -import { OnEvent } from '@nestjs/event-emitter'; +import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'; import { PrismaService } from 'src/prisma/prisma.service'; import { PICTURE } from 'src/profile/dto/profile.dto'; @Injectable() export class GameService { - constructor(private readonly prisma: PrismaService) { - // this.launchGame(); + constructor( + private readonly prisma: PrismaService, + private eventEmitter: EventEmitter2, + ) { + this.launchGame(); } private waitingPlayers: string[] = []; @@ -19,13 +22,14 @@ export class GameService { private launchGame() { setInterval(() => { - console.log('waitingPlayers'); + console.log('waitingPlayers', this.waitingPlayers.length); if (this.waitingPlayers.length >= 2) { console.log('Game launched!'); const two_players = this.waitingPlayers.splice(0, 2); - console.log(two_players); + this.eventEmitter.emit('game.launched', two_players); + // console.log(two_players); } - }, 1000); + }, 5027); } async getHistory(userId: string, offset: number, limit: number) { diff --git a/backend/code/src/game/game.ts b/backend/code/src/game/game.ts new file mode 100644 index 0000000..2abbe0d --- /dev/null +++ b/backend/code/src/game/game.ts @@ -0,0 +1,35 @@ +import { Socket } from 'socket.io'; + +export class Game { + private async loop() { + console.log('loop'); + await this.sleep(5000); + this.loop(); + } + + start(gameid: string) { + console.log('game started', gameid); + this.loop(); + } + + setplayerScokets(p1socket: Socket, p2socket: Socket) { + this.p1socket = p1socket; + this.p2socket = p2socket; + + this.p1socket.on('move', (data) => { + console.log(data); + }); + this.p2socket.on('move', (data) => { + console.log(data); + }); + } + + private sleep(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); + } + + private p1socket: Socket; + private p2socket: Socket; +} diff --git a/backend/code/src/gateways/gateways.gateway.ts b/backend/code/src/gateways/gateways.gateway.ts index ad2fee1..1280839 100644 --- a/backend/code/src/gateways/gateways.gateway.ts +++ b/backend/code/src/gateways/gateways.gateway.ts @@ -9,6 +9,7 @@ import { MessageFormatDto } from 'src/messages/dto/message-format.dto'; import {} from '@nestjs/platform-socket.io'; import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'; import { PrismaService } from 'src/prisma/prisma.service'; +import { Game } from 'src/game/game'; @WebSocketGateway(3004, { cors: { origin: ['http://localhost:3001'], @@ -20,6 +21,8 @@ export class Gateways implements OnGatewayConnection { private prisma: PrismaService, private readonly eventEmitter: EventEmitter2, ) {} + + private games_map = new Map(); handleConnection(client: Socket) { const userId = client.data.user.sub; const rooms = this.prisma.roomMember.findMany({ @@ -65,9 +68,14 @@ export class Gateways implements OnGatewayConnection { @OnEvent('game.launched') handleGameLaunchedEvent(clients: any) { const game_channel = `Game:${clients[0].id}:${clients[1].id}`; + console.log(game_channel); clients.forEach((client: any) => { client.join(game_channel); }); + const new_game = new Game(); + new_game.setplayerScokets(clients[0], clients[1]); + new_game.start(game_channel); + this.games_map.set(game_channel, new_game); this.server.to(game_channel).emit('game.launched', game_channel); } } From ada200451de6d73904b23513adffb1a18690e388 Mon Sep 17 00:00:00 2001 From: Saad eddyne El abdari Date: Fri, 3 Nov 2023 15:13:25 +0100 Subject: [PATCH 2/7] add: initiating game logic, db seeder fix: change friend lists dtos, add removing users related data after removing profile --- backend/code/accountPickerCli.ts | 76 +++ backend/code/package-lock.json | 478 +++++++++++++++++- backend/code/package.json | 10 +- backend/code/prisma/dbml/schema.dbml | 18 +- backend/code/prisma/schema.prisma | 18 +- backend/code/seeder.ts | 116 +++++ backend/code/src/app.controller.ts | 76 ++- .../src/friends/dto/friend-profile.dto.ts | 34 ++ .../code/src/friends/friends.controller.ts | 25 +- backend/code/src/friends/friends.service.ts | 35 +- backend/code/src/game/game.controller.ts | 2 + backend/code/src/game/game.service.ts | 7 +- backend/code/src/game/game.ts | 26 +- backend/code/src/gateways/gateways.gateway.ts | 13 +- backend/code/src/main.ts | 2 + .../code/src/messages/messages.controller.ts | 3 +- backend/code/src/users/users.controller.ts | 2 + 17 files changed, 810 insertions(+), 131 deletions(-) create mode 100644 backend/code/accountPickerCli.ts create mode 100644 backend/code/seeder.ts create mode 100644 backend/code/src/friends/dto/friend-profile.dto.ts diff --git a/backend/code/accountPickerCli.ts b/backend/code/accountPickerCli.ts new file mode 100644 index 0000000..0bf18d9 --- /dev/null +++ b/backend/code/accountPickerCli.ts @@ -0,0 +1,76 @@ +import * as figlet from 'figlet'; +import * as clipboard from 'clipboardy'; +import * as fs from 'fs'; +import * as readline from 'node:readline'; +(async () => { + const header = figlet.textSync('Account Picker', { + font: 'Roman', + horizontalLayout: 'default', + verticalLayout: 'default', + }); + + console.log(header); + + const users = fs + .readFileSync('./users.txt', 'utf8') + .trim() + .split('\n') + .map((user) => { + const [email, password] = user.split(':'); + return { email, password }; + }); + + // list users to choose from + const userChoices = users.map((user, index) => { + return { + name: `${user.email} (${user.password})`, + value: index, + }; + }); + + // list uset userChoices + // + for (const userChoice of userChoices) { + console.log(`${userChoice.value}: ${userChoice.name}`); + } + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + const question = (query: string) => + new Promise((resolve) => + rl.question(query, (ans) => { + resolve(ans); + }), + ); + + const userIndex = await question('User index: '); + const user = users[parseInt(userIndex as string, 10)]; + console.log(`You chose: ${user.email} (${user.password})`); + rl.close(); + + const codeTemplate = ` +var myHeaders = new Headers(); +myHeaders.append("Content-Type", "application/json"); + +var raw = JSON.stringify({ + "email": "${user.email}", + "password": "${user.password}" +}); + +var requestOptions = { + method: 'POST', + headers: myHeaders, + body: raw, + redirect: 'follow' +}; + +fetch("http://localhost:3001/auth/login", requestOptions) + .then(response => response.text()) + .then(result => console.log(result)) + .catch(error => console.log('error', error));`; + + clipboard.default.writeSync("ksksk") +})(); diff --git a/backend/code/package-lock.json b/backend/code/package-lock.json index 90bc4b7..ddcfeda 100644 --- a/backend/code/package-lock.json +++ b/backend/code/package-lock.json @@ -1,11 +1,11 @@ { - "name": "hands_ondirt", + "name": "PongGame", "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "hands_ondirt", + "name": "PongGame", "version": "0.0.1", "license": "UNLICENSED", "dependencies": { @@ -43,30 +43,38 @@ "unique-username-generator": "^1.2.0" }, "devDependencies": { + "@faker-js/faker": "^8.2.0", "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.0.0", "@types/bcrypt": "^5.0.0", "@types/cookie-parser": "^1.4.4", "@types/express": "^4.17.17", + "@types/figlet": "^1.5.7", "@types/jest": "^29.5.2", "@types/multer": "^1.4.8", "@types/node": "^20.3.1", "@types/passport-jwt": "^3.0.9", + "@types/prompt": "^1.1.7", + "@types/prompt-sync": "^4.2.2", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", + "clipboardy": "^4.0.0", + "copy-to-clipboard": "^3.3.3", "eslint": "^8.42.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.0", + "figlet": "^1.7.0", "jest": "^29.5.0", "prettier": "^3.0.0", "prisma-dbml-generator": "^0.10.0", + "prompt": "^1.3.0", + "prompt-sync": "^4.2.0", "source-map-support": "^0.5.21", "supertest": "^6.3.3", "ts-jest": "^29.1.0", "ts-loader": "^9.4.3", - "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", "typescript": "^5.1.3" } @@ -862,7 +870,6 @@ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, - "optional": true, "engines": { "node": ">=0.1.90" } @@ -872,6 +879,8 @@ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -884,6 +893,8 @@ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -967,6 +978,22 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@faker-js/faker": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.2.0.tgz", + "integrity": "sha512-VacmzZqVxdWdf9y64lDOMZNDMM/FQdtM9IsaOPKOm2suYwEatb8VkdHqOzXcDnZbk7YDE2BmsJmy/2Hmkn563g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0", + "npm": ">=6.14.13" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.11", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", @@ -1668,6 +1695,15 @@ } } }, + "node_modules/@nestjs/cli/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/@nestjs/cli/node_modules/typescript": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", @@ -2599,25 +2635,33 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.1", @@ -2784,6 +2828,12 @@ "@types/send": "*" } }, + "node_modules/@types/figlet": { + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/@types/figlet/-/figlet-1.5.7.tgz", + "integrity": "sha512-0+XwDLeH346mAl3fmw/AaEctBrhkcJl0wZezoNUQ5Tg6J5YW7xL2v9hH3Yg5L4dk/sogEdQexTrNncqjGKwoVw==", + "dev": true + }, "node_modules/@types/graceful-fs": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", @@ -2915,6 +2965,22 @@ "@types/passport": "*" } }, + "node_modules/@types/prompt": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@types/prompt/-/prompt-1.1.7.tgz", + "integrity": "sha512-RNOOfuVLljqRTeYpBZ12P+7mDnbEtDmsoPlOnCM+Yv2qo7+rgGZ5Q0tlqNq5OMkBKM7nGjWwlXoiTeDIjYgcdw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/revalidator": "*" + } + }, + "node_modules/@types/prompt-sync": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/prompt-sync/-/prompt-sync-4.2.2.tgz", + "integrity": "sha512-S8GBVdrchd7egtwrWtnhzZ3mFKgwlFpOCsiemzYgd5Bg75SwR5RquDlE7G4ijPbbNtAATpdn/Km3u5PEAnTsAw==", + "dev": true + }, "node_modules/@types/qrcode": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.4.tgz", @@ -2941,6 +3007,12 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "dev": true }, + "node_modules/@types/revalidator": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@types/revalidator/-/revalidator-0.3.11.tgz", + "integrity": "sha512-kBfaF293K6jZJy8L3PHnxzf05G219vzifhgzbZP4P5NuDMx5HvBu2NJlNvnGD8GXI8ojzY/oypmZE55Q8uS0Ew==", + "dev": true + }, "node_modules/@types/semver": { "version": "7.5.1", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", @@ -3412,6 +3484,8 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.4.0" } @@ -3636,7 +3710,9 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/argparse": { "version": "2.0.1", @@ -4430,6 +4506,172 @@ "node": ">= 10" } }, + "node_modules/clipboardy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-4.0.0.tgz", + "integrity": "sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==", + "dev": true, + "dependencies": { + "execa": "^8.0.1", + "is-wsl": "^3.1.0", + "is64bit": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/clipboardy/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/clipboardy/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/clipboardy/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -4540,6 +4782,15 @@ "color-support": "bin.js" } }, + "node_modules/colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -4552,15 +4803,6 @@ "node": ">= 0.8" } }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/comment-json": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.3.tgz", @@ -4711,6 +4953,15 @@ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "dev": true, + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/core-js": { "version": "3.32.2", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.2.tgz", @@ -4797,7 +5048,9 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/cron-parser": { "version": "4.9.0", @@ -4841,6 +5094,15 @@ "node": ">=8" } }, + "node_modules/cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -5170,6 +5432,8 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.3.1" } @@ -5828,6 +6092,15 @@ "node": ">=4" } }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "dev": true, + "engines": { + "node": "> 0.1.90" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5897,6 +6170,18 @@ "bser": "2.1.1" } }, + "node_modules/figlet": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.7.0.tgz", + "integrity": "sha512-gO8l3wvqo0V7wEFLXPbkX83b7MVjRrk1oRLfYlZXol8nEpb/ON9pcKLI4qpBv5YtOTfrINtqb7b40iYY2FTWFg==", + "dev": true, + "bin": { + "figlet": "bin/index.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -6978,6 +7263,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is64bit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is64bit/-/is64bit-2.0.0.tgz", + "integrity": "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==", + "dev": true, + "dependencies": { + "system-architecture": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -6989,6 +7289,12 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", @@ -9039,6 +9345,58 @@ "node": ">=0.4.0" } }, + "node_modules/prompt": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.3.0.tgz", + "integrity": "sha512-ZkaRWtaLBZl7KKAKndKYUL8WqNT+cQHKRZnT4RYYms48jQkFw3rrBL+/N5K/KtdEveHkxs982MX2BkDKub2ZMg==", + "dev": true, + "dependencies": { + "@colors/colors": "1.5.0", + "async": "3.2.3", + "read": "1.0.x", + "revalidator": "0.1.x", + "winston": "2.x" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/prompt-sync": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", + "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", + "dev": true, + "dependencies": { + "strip-ansi": "^5.0.0" + } + }, + "node_modules/prompt-sync/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/prompt-sync/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/prompt/node_modules/async": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "dev": true + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -9300,6 +9658,18 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -9630,6 +10000,15 @@ "node": ">=0.10.0" } }, + "node_modules/revalidator": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", + "integrity": "sha512-xcBILK2pA9oh4SiinPEZfhP8HfrB/ha+a2fTMyl7Om2WjlDVrOQy99N2MXXlUHqGJz4qEu2duXxHJjDWuK/0xg==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/rimraf": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", @@ -10110,6 +10489,15 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -10356,6 +10744,18 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/system-architecture": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz", + "integrity": "sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -10722,6 +11122,12 @@ "node": ">=8.0" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "dev": true + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -10823,6 +11229,8 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -11118,7 +11526,9 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/v8-to-istanbul": { "version": "9.1.0", @@ -11388,6 +11798,32 @@ "node": ">=8.12.0" } }, + "node_modules/winston": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.7.tgz", + "integrity": "sha512-vLB4BqzCKDnnZH9PHGoS2ycawueX4HLqENXQitvFHczhgW2vFpSOn31LZtVr1KU8YTw7DS4tM+cqyovxo8taVg==", + "dev": true, + "dependencies": { + "async": "^2.6.4", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "stack-trace": "0.0.x" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/winston/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -11503,6 +11939,8 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=6" } diff --git a/backend/code/package.json b/backend/code/package.json index 04d574c..ecdbc50 100644 --- a/backend/code/package.json +++ b/backend/code/package.json @@ -59,30 +59,38 @@ "unique-username-generator": "^1.2.0" }, "devDependencies": { + "@faker-js/faker": "^8.2.0", "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.0.0", "@types/bcrypt": "^5.0.0", "@types/cookie-parser": "^1.4.4", "@types/express": "^4.17.17", + "@types/figlet": "^1.5.7", "@types/jest": "^29.5.2", "@types/multer": "^1.4.8", "@types/node": "^20.3.1", "@types/passport-jwt": "^3.0.9", + "@types/prompt": "^1.1.7", + "@types/prompt-sync": "^4.2.2", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", + "clipboardy": "^4.0.0", + "copy-to-clipboard": "^3.3.3", "eslint": "^8.42.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.0", + "figlet": "^1.7.0", "jest": "^29.5.0", "prettier": "^3.0.0", "prisma-dbml-generator": "^0.10.0", + "prompt": "^1.3.0", + "prompt-sync": "^4.2.0", "source-map-support": "^0.5.21", "supertest": "^6.3.3", "ts-jest": "^29.1.0", "ts-loader": "^9.4.3", - "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", "typescript": "^5.1.3" }, diff --git a/backend/code/prisma/dbml/schema.dbml b/backend/code/prisma/dbml/schema.dbml index 58f63be..c876410 100644 --- a/backend/code/prisma/dbml/schema.dbml +++ b/backend/code/prisma/dbml/schema.dbml @@ -133,24 +133,24 @@ Enum RoomType { dm } -Ref: friends.fromId > users.userId +Ref: friends.fromId > users.userId [delete: Cascade] -Ref: friends.toId > users.userId +Ref: friends.toId > users.userId [delete: Cascade] -Ref: blocked_friends.blocked_by_id > users.userId +Ref: blocked_friends.blocked_by_id > users.userId [delete: Cascade] -Ref: blocked_friends.blocked_id > users.userId +Ref: blocked_friends.blocked_id > users.userId [delete: Cascade] -Ref: matches.participant1Id > users.userId +Ref: matches.participant1Id > users.userId [delete: Cascade] -Ref: matches.participant2Id > users.userId +Ref: matches.participant2Id > users.userId [delete: Cascade] Ref: messages.roomId > rooms.id [delete: Cascade] -Ref: messages.authorId > users.userId +Ref: messages.authorId > users.userId [delete: Cascade] -Ref: rooms.ownerId > users.userId +Ref: rooms.ownerId > users.userId [delete: Cascade] -Ref: room_members.userId > users.userId +Ref: room_members.userId > users.userId [delete: Cascade] Ref: room_members.roomId > rooms.id [delete: Cascade] \ No newline at end of file diff --git a/backend/code/prisma/schema.prisma b/backend/code/prisma/schema.prisma index ceaf261..bba7188 100644 --- a/backend/code/prisma/schema.prisma +++ b/backend/code/prisma/schema.prisma @@ -54,8 +54,8 @@ model Friend { fromId String toId String accepted Boolean @default(false) - from User @relation("from", fields: [fromId], references: [userId]) - to User @relation("to", fields: [toId], references: [userId]) + from User @relation("from", fields: [fromId], references: [userId], onDelete: Cascade) + to User @relation("to", fields: [toId], references: [userId], onDelete: Cascade) @@unique([fromId, toId], name: "unique_friend") @@map("friends") @@ -66,9 +66,9 @@ model BlockedUsers { createdAt DateTime @default(now()) id String @id - Blcoked_by User @relation("blocked_by", fields: [blocked_by_id], references: [userId]) + Blcoked_by User @relation("blocked_by", fields: [blocked_by_id], references: [userId], onDelete: Cascade) blocked_by_id String @unique - Blocked User @relation("blocked", fields: [blocked_id], references: [userId]) + Blocked User @relation("blocked", fields: [blocked_id], references: [userId], onDelete: Cascade) blocked_id String @unique dmRoomId String? @@ -86,8 +86,8 @@ model Match { score1 Int? score2 Int? gametype String? - participant1 User @relation("participant1", fields: [participant1Id], references: [userId]) - participant2 User @relation("participant2", fields: [participant2Id], references: [userId]) + participant1 User @relation("participant1", fields: [participant1Id], references: [userId], onDelete: Cascade) + participant2 User @relation("participant2", fields: [participant2Id], references: [userId], onDelete: Cascade) @@map("matches") } @@ -99,7 +99,7 @@ model Message { id String @id @default(cuid()) authorId String room Room @relation(fields: [roomId], references: [id], onDelete: Cascade) - author User @relation(fields: [authorId], references: [userId]) + author User @relation(fields: [authorId], references: [userId], onDelete: Cascade) roomId String content String? @@ -116,7 +116,7 @@ model Room { ownerId String type RoomType @default(public) password String? - owner User @relation("owner", fields: [ownerId], references: [userId]) + owner User @relation("owner", fields: [ownerId], references: [userId], onDelete: Cascade) members RoomMember[] messages Message[] @@ -135,7 +135,7 @@ model RoomMember { bannedAt DateTime? is_mueted Boolean @default(false) mute_expires DateTime? - user User @relation(fields: [userId], references: [userId]) + user User @relation(fields: [userId], references: [userId], onDelete: Cascade) room Room @relation(fields: [roomId], references: [id], onDelete: Cascade) @@unique([userId, roomId], name: "unique_user_room") diff --git a/backend/code/seeder.ts b/backend/code/seeder.ts new file mode 100644 index 0000000..5f6d6be --- /dev/null +++ b/backend/code/seeder.ts @@ -0,0 +1,116 @@ +import { PrismaClient, User } from '@prisma/client'; +import { faker } from '@faker-js/faker'; +import * as bcrypt from 'bcrypt'; +import * as fs from 'fs'; + +class dataSeeder extends PrismaClient { + constructor() { + super({ + datasources: { + db: { + url: process.env.DATABASE_URL, + }, + }, + }); + } + + async seed() { + await this.$connect(); + const users = faker.helpers.multiple(this.createRandomUser, { count: 10 }); + const registeredUsers = await this.registerUser(users); + await this.makeFriendship(registeredUsers); + await this.makeMatch(registeredUsers); + await this.$disconnect(); + } + + private async registerUser(users: any): Promise { + if (fs.existsSync('users.txt')) { + console.log('users.txt exists'); + fs.unlinkSync('users.txt'); + } + const new_users: User[] = []; + for await (const user of users) { + fs.appendFile('users.txt', `${user.email}:${user.password}\n`, (err) => { + if (err) throw err; + }); + + const hash = await bcrypt.hash(user.password, 10); + + const new_user = await this.user.create({ + data: { + email: user.email, + password: hash, + Username: user.username, + firstName: user.firstName, + lastName: user.lastName, + avatar: user.avatar, + discreption: user.bio, + }, + }); + new_users.push(new_user); + } + return new_users; + } + + private async makeFriendship(users: User[]) { + for await (const user of users) { + const randomUser = users[Math.floor(Math.random() * users.length)]; + if (randomUser.userId !== user.userId) { + const friendshipid = [user.userId, randomUser.userId].sort().join('-'); + await this.friend.upsert({ + where: { + id: friendshipid, + }, + create: { + id: friendshipid, + from: { + connect: { + userId: user.userId, + }, + }, + to: { + connect: { + userId: randomUser.userId, + }, + }, + accepted: true, + }, + update: {}, + }); + } + } + } + + private async makeMatch(users: User[]) { + for await (const user of users) { + const randomUser = users[Math.floor(Math.random() * users.length)]; + if (randomUser.userId !== user.userId) { + await this.match.create({ + data: { + participant1Id: user.userId, + participant2Id: randomUser.userId, + winner_id: Math.random() > 0.5 ? user.userId : randomUser.userId, + score1: Math.floor(Math.random() * 10), + score2: Math.floor(Math.random() * 10), + }, + }); + } + } + } + + private createRandomUser() { + return { + username: faker.internet.userName(), + email: faker.internet.email(), + avatar: 'v1698656518/nest-blog/clocnzgx80006q73seyj9hzx5.png', + password: faker.internet.password(), + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + bio: faker.person.bio(), + }; + } +} + +(async () => { + await new dataSeeder().seed(); +})(); diff --git a/backend/code/src/app.controller.ts b/backend/code/src/app.controller.ts index f563d8c..c7b9b70 100644 --- a/backend/code/src/app.controller.ts +++ b/backend/code/src/app.controller.ts @@ -31,51 +31,49 @@ export class AppController { @Get() // @UseGuards(AuthenticatedGuard) home() { - return ` + return ` + - - - - - - - - - + -socket.on('error', (error) => { -}); -}); + + -socket.on('disconnect', () => { -}); + - + // Listen for events from the server + socket.on("game.launched", (data) => { + console.log("Received from server:", data); + }); + + `; } diff --git a/backend/code/src/friends/dto/friend-profile.dto.ts b/backend/code/src/friends/dto/friend-profile.dto.ts new file mode 100644 index 0000000..059c0c3 --- /dev/null +++ b/backend/code/src/friends/dto/friend-profile.dto.ts @@ -0,0 +1,34 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { User } from '@prisma/client'; +import { PICTURE } from 'src/profile/dto/profile.dto'; + +export class FriendProfileDto { + constructor(friend: Partial) { + this.userId = friend?.userId; + this.firstname = friend?.firstName; + this.lastname = friend?.lastName; + this.avatar = { + thumbnail: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_48,w_48/${friend.avatar}`, + medium: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_72,w_72/${friend.avatar}`, + large: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_128,w_128/${friend.avatar}`, + }; + } + + @ApiProperty({ example: 'cloh36sfy00002v6laxvhdj7r' }) + userId: string; + @ApiProperty({ example: 'John' }) + firstname: string; + @ApiProperty({ example: 'Doe' }) + lastname: string; + @ApiProperty({ + example: { + thumbnail: + 'https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_48,w_48/cloh36sfy00002v6laxvhdj7r', + medium: + 'https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_72,w_72/cloh36sfy00002v6laxvhdj7r', + large: + 'https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_128,w_128/cloh36sfy00002v6laxvhdj7r', + }, + }) + avatar: PICTURE; +} diff --git a/backend/code/src/friends/friends.controller.ts b/backend/code/src/friends/friends.controller.ts index d140cf9..8c47b7b 100644 --- a/backend/code/src/friends/friends.controller.ts +++ b/backend/code/src/friends/friends.controller.ts @@ -12,22 +12,10 @@ import { FriendsService } from './friends.service'; import { AtGuard } from 'src/auth/guards/at.guard'; import { FriendDto } from './dto/add-friend.dto'; import { GetCurrentUser } from 'src/auth/decorator/get_current_user.decorator'; -import { - ApiCookieAuth, - ApiProperty, - ApiQuery, - ApiResponse, - ApiTags, -} from '@nestjs/swagger'; +import { ApiCookieAuth, ApiResponse, ApiTags } from '@nestjs/swagger'; import { FriendResponseDto } from './dto/frined-response.dto'; import { QueryOffsetDto } from './dto/query-ofsset-dto'; - -class ListType { - @ApiProperty({ example: '60f1a7b0e1b3c2a4e8b4a1a0' }) - userId: string; - firstName: string; - lastName: string; -} +import { FriendProfileDto } from './dto/friend-profile.dto'; @ApiTags('friends') @ApiCookieAuth('X-Acces-Token') @@ -107,9 +95,7 @@ export class FriendsController { @Get('list') @UseGuards(AtGuard) - @ApiQuery({ name: 'offset', required: false, type: Number }) - @ApiQuery({ name: 'limit', required: false, type: Number }) - @ApiResponse({ type: ListType }) + @ApiResponse({ type: FriendProfileDto }) async getFriendsList( @GetCurrentUser('userId') userId: string, @Query() { offset, limit }: QueryOffsetDto, @@ -119,8 +105,7 @@ export class FriendsController { @Get('requests') @UseGuards(AtGuard) - @ApiQuery({ name: 'offset', required: false, type: Number }) - @ApiQuery({ name: 'limit', required: false, type: Number }) + @ApiResponse({ type: FriendProfileDto }) async getFriendsRequests( @GetCurrentUser('userId') userId: string, @Query() { offset, limit }: QueryOffsetDto, @@ -130,6 +115,7 @@ export class FriendsController { @Get('blocklist') @UseGuards(AtGuard) + @ApiResponse({ type: FriendProfileDto }) async getBlockList( @GetCurrentUser('userId') userId: string, @Query() { offset, limit }: QueryOffsetDto, @@ -139,6 +125,7 @@ export class FriendsController { @Get('pending') @UseGuards(AtGuard) + @ApiResponse({ type: FriendProfileDto }) async getFriendsPending( @GetCurrentUser('userId') userId: string, @Query() { offset, limit }: QueryOffsetDto, diff --git a/backend/code/src/friends/friends.service.ts b/backend/code/src/friends/friends.service.ts index c591434..594a6fa 100644 --- a/backend/code/src/friends/friends.service.ts +++ b/backend/code/src/friends/friends.service.ts @@ -3,7 +3,7 @@ import { PrismaService } from 'src/prisma/prisma.service'; import { UsersService } from 'src/users/users.service'; import { FriendResponseDto } from './dto/frined-response.dto'; import { EventEmitter2 } from '@nestjs/event-emitter'; -import { PICTURE } from 'src/profile/dto/profile.dto'; +import { FriendProfileDto } from './dto/friend-profile.dto'; @Injectable() export class FriendsService { @@ -225,29 +225,9 @@ export class FriendsService { return friends.map((friend: any) => { if (friend.from.userId === userId) { - friend = friend.to as any; - const avatar: PICTURE = { - thumbnail: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_48,w_48/${friend.avatar}`, - medium: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_72,w_72/${friend.avatar}`, - large: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_128,w_128/${friend.avatar}`, - }; - delete friend.avatar; - return { - ...friend.to, - avatar, - }; + return new FriendProfileDto(friend.to); } else { - friend = friend.from as any; - const avatar: PICTURE = { - thumbnail: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_48,w_48/${friend.avatar}`, - medium: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_72,w_72/${friend.avatar}`, - large: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_128,w_128/${friend.avatar}`, - }; - delete friend.avatar; - return { - ...friend.from, - avatar, - }; + return new FriendProfileDto(friend.from); } }); } @@ -266,11 +246,12 @@ export class FriendsService { userId: true, firstName: true, lastName: true, + avatar: true, }, }, }, }); - return friends.map((friend) => friend.from); + return friends.map((friend) => new FriendProfileDto(friend.from)); } async getBlockList(userId: string, offset: number, limit: number) { @@ -286,11 +267,12 @@ export class FriendsService { userId: true, firstName: true, lastName: true, + avatar: true, }, }, }, }); - return blocked.map((friend) => friend.Blocked); + return blocked.map((friend) => new FriendProfileDto(friend.Blocked)); } async getPendingRequests(userId: string, offset: number, limit: number) { @@ -307,10 +289,11 @@ export class FriendsService { userId: true, firstName: true, lastName: true, + avatar: true, }, }, }, }); - return friends.map((friend) => friend.to); + return friends.map((friend) => new FriendProfileDto(friend.to)); } } diff --git a/backend/code/src/game/game.controller.ts b/backend/code/src/game/game.controller.ts index 4387ede..fcfd5da 100644 --- a/backend/code/src/game/game.controller.ts +++ b/backend/code/src/game/game.controller.ts @@ -3,8 +3,10 @@ import { GameService } from './game.service'; import { AtGuard } from 'src/auth/guards/at.guard'; import { GetCurrentUser } from 'src/auth/decorator/get_current_user.decorator'; import { QueryOffsetDto } from 'src/friends/dto/query-ofsset-dto'; +import { ApiTags } from '@nestjs/swagger'; @Controller('game') +@ApiTags('game') export class GameController { constructor(private readonly gameService: GameService) {} diff --git a/backend/code/src/game/game.service.ts b/backend/code/src/game/game.service.ts index 7c71e23..0efe1ff 100644 --- a/backend/code/src/game/game.service.ts +++ b/backend/code/src/game/game.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@nestjs/common'; import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'; +import { Socket } from 'socket.io'; import { PrismaService } from 'src/prisma/prisma.service'; import { PICTURE } from 'src/profile/dto/profile.dto'; @@ -12,11 +13,11 @@ export class GameService { this.launchGame(); } - private waitingPlayers: string[] = []; + private waitingPlayers: Socket[] = []; @OnEvent('game.start') - handleGameStartEvent(client: any) { - this.waitingPlayers.push(client.id); + handleGameStartEvent(client: Socket) { + this.waitingPlayers.push(client); console.log('client subscribed to the queue'); } diff --git a/backend/code/src/game/game.ts b/backend/code/src/game/game.ts index 2abbe0d..983a944 100644 --- a/backend/code/src/game/game.ts +++ b/backend/code/src/game/game.ts @@ -1,14 +1,17 @@ +import { EventEmitter2 } from '@nestjs/event-emitter'; import { Socket } from 'socket.io'; export class Game { + constructor(private readonly eventEmitter: EventEmitter2) {} private async loop() { console.log('loop'); await this.sleep(5000); this.loop(); } - start(gameid: string) { - console.log('game started', gameid); + start(ngameid: string) { + console.log('game started', ngameid); + this.gameid = ngameid; this.loop(); } @@ -17,11 +20,29 @@ export class Game { this.p2socket = p2socket; this.p1socket.on('move', (data) => { + console.log('heh'); console.log(data); }); this.p2socket.on('move', (data) => { + console.log('heh'); console.log(data); }); + this.p1socket.on('disconnect', () => { + console.log('p1 disconnected'); + this.emitGameEnd('p1 disconnected'); + }); + this.p2socket.on('disconnect', () => { + console.log('p2 disconnected'); + this.emitGameEnd('p2 disconnected'); + }); + } + + private emitGameEnd(message: string) { + console.log('game end'); + this.eventEmitter.emit('game.end', { + message: message, + gameid: this.gameid, + }); } private sleep(ms: number) { @@ -30,6 +51,7 @@ export class Game { }); } + private gameid: string; private p1socket: Socket; private p2socket: Socket; } diff --git a/backend/code/src/gateways/gateways.gateway.ts b/backend/code/src/gateways/gateways.gateway.ts index 1280839..4bc1050 100644 --- a/backend/code/src/gateways/gateways.gateway.ts +++ b/backend/code/src/gateways/gateways.gateway.ts @@ -62,20 +62,29 @@ export class Gateways implements OnGatewayConnection { @SubscribeMessage('startGame') handleGameStartEvent(client: Socket) { + console.log(client.data.user); this.eventEmitter.emit('game.start', client); } @OnEvent('game.launched') handleGameLaunchedEvent(clients: any) { const game_channel = `Game:${clients[0].id}:${clients[1].id}`; - console.log(game_channel); + console.log(game_channel); clients.forEach((client: any) => { client.join(game_channel); }); - const new_game = new Game(); + const new_game = new Game(this.eventEmitter); new_game.setplayerScokets(clients[0], clients[1]); new_game.start(game_channel); this.games_map.set(game_channel, new_game); this.server.to(game_channel).emit('game.launched', game_channel); } + + @OnEvent('game.end') + handleGameEndEvent(data: any) { + console.log('game ended'); + console.log(data); + this.server.to(data.gameid).emit('game.end', data); + this.games_map.delete(data.gameid); + } } diff --git a/backend/code/src/main.ts b/backend/code/src/main.ts index 44be4ac..c601abb 100644 --- a/backend/code/src/main.ts +++ b/backend/code/src/main.ts @@ -45,6 +45,8 @@ async function bootstrap() { .addTag('friends') .addTag('rooms') .addTag('Messages') + .addTag('user') + .addTag('game') .build(); const document = SwaggerModule.createDocument(app, options); SwaggerModule.setup('api', app, document); diff --git a/backend/code/src/messages/messages.controller.ts b/backend/code/src/messages/messages.controller.ts index 2a295b7..47a2a62 100644 --- a/backend/code/src/messages/messages.controller.ts +++ b/backend/code/src/messages/messages.controller.ts @@ -11,10 +11,11 @@ import { MessagesService } from './messages.service'; import { CreateMessgaeDto } from './dto/create-messgae.dto'; import { GetCurrentUser } from 'src/auth/decorator/get_current_user.decorator'; import { AtGuard } from 'src/auth/guards/at.guard'; -import { ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { ApiCookieAuth, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger'; import { MessageFormatDto } from './dto/message-format.dto'; import { QueryOffsetDto } from 'src/friends/dto/query-ofsset-dto'; +@ApiCookieAuth('X-Acces-Token') @ApiTags('Messages') @Controller('messages') export class MessagesController { diff --git a/backend/code/src/users/users.controller.ts b/backend/code/src/users/users.controller.ts index 929cfe5..06983a5 100644 --- a/backend/code/src/users/users.controller.ts +++ b/backend/code/src/users/users.controller.ts @@ -14,7 +14,9 @@ import { usersSearchDto } from './dto/search-user.dto'; import { TwoFactorDto } from './dto/two-factor.dto'; import { GetCurrentUser } from 'src/auth/decorator/get_current_user.decorator'; import { AuthGuard } from '@nestjs/passport'; +import { ApiTags } from '@nestjs/swagger'; +@ApiTags('user') @Controller('users') export class UsersController { constructor(private readonly usersService: UsersService) {} From ee9ec102ce000c561c482e029e8ef6d3b97e2ab9 Mon Sep 17 00:00:00 2001 From: sedeve Date: Sat, 4 Nov 2023 11:24:22 +0100 Subject: [PATCH 3/7] init: prevent banned users from getiing latest message from room and recieving new ones while using same session --- backend/code/.gitignore | 4 +- backend/code/accountPickerCli.ts | 4 +- backend/code/package-lock.json | 41 +++--------- backend/code/package.json | 1 + backend/code/seeder.ts | 1 + backend/code/src/app.controller.ts | 65 ++++++++++++++++--- backend/code/src/gateways/gateways.gateway.ts | 5 ++ backend/code/src/rooms/rooms.service.ts | 5 ++ 8 files changed, 80 insertions(+), 46 deletions(-) diff --git a/backend/code/.gitignore b/backend/code/.gitignore index e47273e..0e4fb52 100644 --- a/backend/code/.gitignore +++ b/backend/code/.gitignore @@ -32,4 +32,6 @@ lerna-debug.log* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json -!.vscode/extensions.json \ No newline at end of file +!.vscode/extensions.json + +users.txt diff --git a/backend/code/accountPickerCli.ts b/backend/code/accountPickerCli.ts index 0bf18d9..ad90f7f 100644 --- a/backend/code/accountPickerCli.ts +++ b/backend/code/accountPickerCli.ts @@ -1,5 +1,4 @@ import * as figlet from 'figlet'; -import * as clipboard from 'clipboardy'; import * as fs from 'fs'; import * as readline from 'node:readline'; (async () => { @@ -71,6 +70,5 @@ fetch("http://localhost:3001/auth/login", requestOptions) .then(response => response.text()) .then(result => console.log(result)) .catch(error => console.log('error', error));`; - - clipboard.default.writeSync("ksksk") + console.log(codeTemplate); })(); diff --git a/backend/code/package-lock.json b/backend/code/package-lock.json index ddcfeda..9234f8e 100644 --- a/backend/code/package-lock.json +++ b/backend/code/package-lock.json @@ -75,6 +75,7 @@ "supertest": "^6.3.3", "ts-jest": "^29.1.0", "ts-loader": "^9.4.3", + "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", "typescript": "^5.1.3" } @@ -879,8 +880,6 @@ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -893,8 +892,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -2635,33 +2632,25 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@types/babel__core": { "version": "7.20.1", @@ -3484,8 +3473,6 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">=0.4.0" } @@ -3710,9 +3697,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/argparse": { "version": "2.0.1", @@ -5048,9 +5033,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/cron-parser": { "version": "4.9.0", @@ -5432,8 +5415,6 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">=0.3.1" } @@ -11229,8 +11210,6 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -11526,9 +11505,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/v8-to-istanbul": { "version": "9.1.0", @@ -11939,8 +11916,6 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">=6" } diff --git a/backend/code/package.json b/backend/code/package.json index ecdbc50..f131466 100644 --- a/backend/code/package.json +++ b/backend/code/package.json @@ -91,6 +91,7 @@ "supertest": "^6.3.3", "ts-jest": "^29.1.0", "ts-loader": "^9.4.3", + "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", "typescript": "^5.1.3" }, diff --git a/backend/code/seeder.ts b/backend/code/seeder.ts index 5f6d6be..6d1bfc4 100644 --- a/backend/code/seeder.ts +++ b/backend/code/seeder.ts @@ -45,6 +45,7 @@ class dataSeeder extends PrismaClient { lastName: user.lastName, avatar: user.avatar, discreption: user.bio, + profileFinished: true, }, }); new_users.push(new_user); diff --git a/backend/code/src/app.controller.ts b/backend/code/src/app.controller.ts index c7b9b70..482f912 100644 --- a/backend/code/src/app.controller.ts +++ b/backend/code/src/app.controller.ts @@ -32,6 +32,7 @@ export class AppController { // @UseGuards(AuthenticatedGuard) home() { return ` + @@ -45,33 +46,79 @@ export class AppController { +
    + + +
    + + + + + +
    + diff --git a/backend/code/src/gateways/gateways.gateway.ts b/backend/code/src/gateways/gateways.gateway.ts index 4bc1050..6069e0e 100644 --- a/backend/code/src/gateways/gateways.gateway.ts +++ b/backend/code/src/gateways/gateways.gateway.ts @@ -87,4 +87,9 @@ export class Gateways implements OnGatewayConnection { this.server.to(data.gameid).emit('game.end', data); this.games_map.delete(data.gameid); } + + @SubscribeMessage('roomDeparture') + hundleDeparture(client: Socket, data: { roomid: string; message: string }) { + console.log(data); + } } diff --git a/backend/code/src/rooms/rooms.service.ts b/backend/code/src/rooms/rooms.service.ts index 52b5e12..bc79958 100644 --- a/backend/code/src/rooms/rooms.service.ts +++ b/backend/code/src/rooms/rooms.service.ts @@ -550,6 +550,8 @@ export class RoomsService { }, select: { is_admin: true, + is_banned: true, + bannedAt: true, }, }, }), @@ -580,6 +582,9 @@ export class RoomsService { const last_message = await this.prisma.message.findFirst({ where: { roomId: room.id, + ...(room.members[0].is_banned && { + createdAt: { lte: room.members[0].bannedAt }, + }), }, orderBy: { createdAt: 'desc', From 4dc4c94d902f2a3c1e674ec577fc05758f57fdea Mon Sep 17 00:00:00 2001 From: Saad eddyne El abdari Date: Sat, 4 Nov 2023 18:55:28 +0100 Subject: [PATCH 4/7] sync --- backend/code/seeder.ts | 45 ++++ backend/code/src/app.controller.ts | 196 ++++++++++++++---- backend/code/src/game/game.service.ts | 2 +- backend/code/src/gateways/gateways.gateway.ts | 17 +- backend/code/src/messages/messages.service.ts | 5 +- backend/code/src/rooms/rooms.controller.ts | 10 + backend/code/src/rooms/rooms.service.ts | 36 ++++ 7 files changed, 268 insertions(+), 43 deletions(-) diff --git a/backend/code/seeder.ts b/backend/code/seeder.ts index 6d1bfc4..a1148dd 100644 --- a/backend/code/seeder.ts +++ b/backend/code/seeder.ts @@ -20,6 +20,7 @@ class dataSeeder extends PrismaClient { const registeredUsers = await this.registerUser(users); await this.makeFriendship(registeredUsers); await this.makeMatch(registeredUsers); + await this.createRoom(registeredUsers); await this.$disconnect(); } @@ -110,6 +111,50 @@ class dataSeeder extends PrismaClient { bio: faker.person.bio(), }; } + + private async createRoom(users: User[]) { + const owner = users[Math.floor(Math.random() * users.length)]; + const roomData = { + name: faker.lorem.word(), + ownerId: owner.userId, + }; + + // add random users to the room + const filtredUsers = users.filter((user) => user.userId !== owner.userId); + const randomUsers = filtredUsers.slice( + 0, + Math.floor(Math.random() * filtredUsers.length), + ); + + const room = await this.room.create({ + data: { + name: roomData.name, + ownerId: roomData.ownerId, + type: 'public', + }, + }); + + for await (const user of randomUsers) { + await this.roomMember.create({ + data: { + userId: user.userId, + roomId: room.id, + is_admin: Math.random() > 0.8 ? true : false, + }, + }); + } + await this.roomMember.create({ + data: { + userId: owner.userId, + roomId: room.id, + is_admin: true, + }, + }); + + console.log("roomid: ", room.id) + console.log("ownerid: ", owner.userId) + console.log("randomUsers: ", randomUsers) + } } (async () => { diff --git a/backend/code/src/app.controller.ts b/backend/code/src/app.controller.ts index 482f912..e70376d 100644 --- a/backend/code/src/app.controller.ts +++ b/backend/code/src/app.controller.ts @@ -38,56 +38,91 @@ export class AppController { + Socket.io Example - - -

    Socket.io Example

    + - - - -
      - - -
      - - - - - -
      - - + + +

      Socket.io Example

      + +
      + + + +
        + + +
        + + + + + +
        + + + + + + +
        + + + + + +
        +
        `; diff --git a/backend/code/src/game/game.service.ts b/backend/code/src/game/game.service.ts index 0efe1ff..6a42cca 100644 --- a/backend/code/src/game/game.service.ts +++ b/backend/code/src/game/game.service.ts @@ -10,7 +10,7 @@ export class GameService { private readonly prisma: PrismaService, private eventEmitter: EventEmitter2, ) { - this.launchGame(); + // this.launchGame(); } private waitingPlayers: Socket[] = []; diff --git a/backend/code/src/gateways/gateways.gateway.ts b/backend/code/src/gateways/gateways.gateway.ts index 6069e0e..4100951 100644 --- a/backend/code/src/gateways/gateways.gateway.ts +++ b/backend/code/src/gateways/gateways.gateway.ts @@ -1,4 +1,5 @@ import { + MessageBody, OnGatewayConnection, SubscribeMessage, WebSocketGateway, @@ -41,6 +42,7 @@ export class Gateways implements OnGatewayConnection { rooms.then((rooms) => { rooms.forEach((room) => { client.join(`Romm:${room.room.id}`); + console.log(`Romm:${room.room.id}`); }); }); client.join(`notif:${userId}`); @@ -89,7 +91,18 @@ export class Gateways implements OnGatewayConnection { } @SubscribeMessage('roomDeparture') - hundleDeparture(client: Socket, data: { roomid: string; message: string }) { - console.log(data); + async hundleDeparture( + @MessageBody() data: { roomId: string; memberId: string }, + ) { + const clients = await this.server.in(`Romm:${data.roomId}`).fetchSockets(); + console.log(`Romm:${data.roomId}`); + const clientToBan = clients.find( + (client) => client.data.user.sub === data.memberId, + ); + if (clientToBan) { + clientToBan.leave(`Romm:${data.roomId}`); + } else { + console.log('client not found'); + } } } diff --git a/backend/code/src/messages/messages.service.ts b/backend/code/src/messages/messages.service.ts index 682f041..024cfc0 100644 --- a/backend/code/src/messages/messages.service.ts +++ b/backend/code/src/messages/messages.service.ts @@ -11,6 +11,7 @@ export class MessagesService { ) {} async sendMessages(userId: string, channelId: string, messageDto: any) { + console.log(messageDto); if (messageDto.content.length > 1000) { throw new HttpException('Message is too long', HttpStatus.BAD_REQUEST); } @@ -27,7 +28,9 @@ export class MessagesService { }, }, }); - + if (!room) { + throw new HttpException('Room not found', HttpStatus.NOT_FOUND); + } if (room.type === 'dm') { const blocked = await this.prisma.blockedUsers.findFirst({ where: { diff --git a/backend/code/src/rooms/rooms.controller.ts b/backend/code/src/rooms/rooms.controller.ts index f2801ab..a781fe2 100644 --- a/backend/code/src/rooms/rooms.controller.ts +++ b/backend/code/src/rooms/rooms.controller.ts @@ -207,4 +207,14 @@ export class RoomsController { ) { return this.roomsService.listRooms(userId, offset, limit, joined); } + + @Get('dms') + @HttpCode(HttpStatus.OK) + @UseGuards(AtGuard) + async getDMs( + @GetCurrentUser('userId') userId: string, + @Query() { offset, limit }: QueryOffsetDto, + ) { + return this.roomsService.getDMs(userId, offset, limit); + } } diff --git a/backend/code/src/rooms/rooms.service.ts b/backend/code/src/rooms/rooms.service.ts index bc79958..c4ee26f 100644 --- a/backend/code/src/rooms/rooms.service.ts +++ b/backend/code/src/rooms/rooms.service.ts @@ -608,4 +608,40 @@ export class RoomsService { ); return roomsData; } + + async getDms(userId: string, offset: number, limit: number) { + const rooms = await this.prisma.room.findMany({ + skip: offset, + take: limit, + where: { + type: 'dm', + members: { + some: { + userId: userId, + }, + }, + }, + select: { + id: true, + type: true, + ownerId: true, + members: { + select: { + userId: true, + }, + }, + }, + }); + return rooms.map((room) => { + const secondMember = room.members.find( + (member) => member.userId !== userId, + ); + return { + id: room.id, + name: secondMember.userId, + type: room.type, + ownerId: room.ownerId, + }; + }); + } } From 939a323b70d0db307e3e1320d752062625a2d8ed Mon Sep 17 00:00:00 2001 From: Saad eddyne El abdari Date: Sun, 5 Nov 2023 16:53:05 +0100 Subject: [PATCH 5/7] add: online presence, handle ban unban and join room in sockets --- backend/code/prisma/dbml/schema.dbml | 1 + .../migration.sql | 56 ++++++++++ backend/code/prisma/schema.prisma | 1 + backend/code/src/gateways/gateways.gateway.ts | 103 ++++++++++++++++-- backend/code/src/rooms/rooms.service.ts | 81 +++++++++++--- 5 files changed, 220 insertions(+), 22 deletions(-) create mode 100644 backend/code/prisma/migrations/20231105142742_add_onlien_status_to_user/migration.sql diff --git a/backend/code/prisma/dbml/schema.dbml b/backend/code/prisma/dbml/schema.dbml index c876410..3681745 100644 --- a/backend/code/prisma/dbml/schema.dbml +++ b/backend/code/prisma/dbml/schema.dbml @@ -11,6 +11,7 @@ Table users { refreshedHash String intraId String [unique] profileFinished Boolean [not null, default: false] + online Boolean [not null, default: false] Username String [unique] firstName String lastName String diff --git a/backend/code/prisma/migrations/20231105142742_add_onlien_status_to_user/migration.sql b/backend/code/prisma/migrations/20231105142742_add_onlien_status_to_user/migration.sql new file mode 100644 index 0000000..5c46879 --- /dev/null +++ b/backend/code/prisma/migrations/20231105142742_add_onlien_status_to_user/migration.sql @@ -0,0 +1,56 @@ +-- DropForeignKey +ALTER TABLE "blocked_friends" DROP CONSTRAINT "blocked_friends_blocked_by_id_fkey"; + +-- DropForeignKey +ALTER TABLE "blocked_friends" DROP CONSTRAINT "blocked_friends_blocked_id_fkey"; + +-- DropForeignKey +ALTER TABLE "friends" DROP CONSTRAINT "friends_fromId_fkey"; + +-- DropForeignKey +ALTER TABLE "friends" DROP CONSTRAINT "friends_toId_fkey"; + +-- DropForeignKey +ALTER TABLE "matches" DROP CONSTRAINT "matches_participant1Id_fkey"; + +-- DropForeignKey +ALTER TABLE "matches" DROP CONSTRAINT "matches_participant2Id_fkey"; + +-- DropForeignKey +ALTER TABLE "messages" DROP CONSTRAINT "messages_authorId_fkey"; + +-- DropForeignKey +ALTER TABLE "room_members" DROP CONSTRAINT "room_members_userId_fkey"; + +-- DropForeignKey +ALTER TABLE "rooms" DROP CONSTRAINT "rooms_ownerId_fkey"; + +-- AlterTable +ALTER TABLE "users" ADD COLUMN "online" BOOLEAN NOT NULL DEFAULT false; + +-- AddForeignKey +ALTER TABLE "friends" ADD CONSTRAINT "friends_fromId_fkey" FOREIGN KEY ("fromId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "friends" ADD CONSTRAINT "friends_toId_fkey" FOREIGN KEY ("toId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "blocked_friends" ADD CONSTRAINT "blocked_friends_blocked_by_id_fkey" FOREIGN KEY ("blocked_by_id") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "blocked_friends" ADD CONSTRAINT "blocked_friends_blocked_id_fkey" FOREIGN KEY ("blocked_id") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "matches" ADD CONSTRAINT "matches_participant1Id_fkey" FOREIGN KEY ("participant1Id") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "matches" ADD CONSTRAINT "matches_participant2Id_fkey" FOREIGN KEY ("participant2Id") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "messages" ADD CONSTRAINT "messages_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "rooms" ADD CONSTRAINT "rooms_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "room_members" ADD CONSTRAINT "room_members_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/backend/code/prisma/schema.prisma b/backend/code/prisma/schema.prisma index bba7188..6e324cd 100644 --- a/backend/code/prisma/schema.prisma +++ b/backend/code/prisma/schema.prisma @@ -24,6 +24,7 @@ model User { refreshedHash String? intraId String? @unique profileFinished Boolean @default(false) + online Boolean @default(false) Username String? @unique firstName String? diff --git a/backend/code/src/gateways/gateways.gateway.ts b/backend/code/src/gateways/gateways.gateway.ts index 4100951..6a8adc3 100644 --- a/backend/code/src/gateways/gateways.gateway.ts +++ b/backend/code/src/gateways/gateways.gateway.ts @@ -1,6 +1,7 @@ import { MessageBody, OnGatewayConnection, + OnGatewayDisconnect, SubscribeMessage, WebSocketGateway, WebSocketServer, @@ -11,20 +12,22 @@ import {} from '@nestjs/platform-socket.io'; import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'; import { PrismaService } from 'src/prisma/prisma.service'; import { Game } from 'src/game/game'; + @WebSocketGateway(3004, { cors: { origin: ['http://localhost:3001'], }, transports: ['websocket'], }) -export class Gateways implements OnGatewayConnection { +export class Gateways implements OnGatewayConnection, OnGatewayDisconnect { constructor( private prisma: PrismaService, private readonly eventEmitter: EventEmitter2, ) {} + @WebSocketServer() private server: Server; private games_map = new Map(); - handleConnection(client: Socket) { + async handleConnection(client: Socket) { const userId = client.data.user.sub; const rooms = this.prisma.roomMember.findMany({ where: { @@ -45,10 +48,48 @@ export class Gateways implements OnGatewayConnection { console.log(`Romm:${room.room.id}`); }); }); - client.join(`notif:${userId}`); + client.join(`User:${userId}`); + + await this.prisma.user.update({ + where: { + userId, + }, + data: { + online: true, + }, + }); + + const frienduserIds = await this.prisma.friend.findMany({ + where: { + OR: [ + { + fromId: userId, + }, + { + toId: userId, + }, + ], + }, + select: { + fromId: true, + toId: true, + }, + }); + + const friendIds = frienduserIds + .map((friend) => (friend.toId === userId ? friend.fromId : friend.toId)) + .filter((id) => this.server.sockets.adapter.rooms.get(`User:${id}`)?.size); + + client.emit('onlineFriends', friendIds); + + this.server.emit('friendOnline', userId); } - @WebSocketServer() private server: Server; + async handleDisconnect(client: Socket) { + const userId = client.data.user.sub; + + this.server.emit('friendOffline', userId); + } @OnEvent('sendMessages') sendMessage(message: MessageFormatDto) { @@ -58,7 +99,7 @@ export class Gateways implements OnGatewayConnection { @OnEvent('addFriendNotif') sendFriendReq(notif: any) { - const channellname: string = `notif:${notif.recipientId}`; + const channellname: string = `User:${notif.recipientId}`; this.server.to(channellname).emit('message', notif); } @@ -90,9 +131,49 @@ export class Gateways implements OnGatewayConnection { this.games_map.delete(data.gameid); } + @SubscribeMessage('joinRoom') + async handleJoinRoomEvent(client: Socket, data: any) { + const member = await this.prisma.roomMember.findFirst({ + where: { + userId: client.data.user.sub, + roomId: data.roomId, + }, + }); + if (member) { + client.join(`Romm:${data.roomId}`); + } + } + + @SubscribeMessage('unban') + async handleUnbanEvent(client: Socket, data: any) { + const owner = await this.prisma.roomMember.findFirst({ + where: { + userId: client.data.user.sub, + roomId: data.roomId, + }, + }); + if (!owner || owner.is_admin === false) { + return; + } + const member = await this.prisma.roomMember.findFirst({ + where: { + userId: data.memberId, + roomId: data.roomId, + }, + }); + if (member) { + const banedClientSocket = await this.server + .in(`User:${data.memberId}`) + .fetchSockets(); + if (banedClientSocket.length > 0) { + banedClientSocket[0].join(`Romm:${data.roomId}`); + } + } + } + @SubscribeMessage('roomDeparture') async hundleDeparture( - @MessageBody() data: { roomId: string; memberId: string }, + @MessageBody() data: { roomId: string; memberId: string; type: string }, ) { const clients = await this.server.in(`Romm:${data.roomId}`).fetchSockets(); console.log(`Romm:${data.roomId}`); @@ -100,9 +181,13 @@ export class Gateways implements OnGatewayConnection { (client) => client.data.user.sub === data.memberId, ); if (clientToBan) { - clientToBan.leave(`Romm:${data.roomId}`); - } else { - console.log('client not found'); + clientToBan.leave(`Romm:${data.roomId}`); + if (data?.type === 'kick') { + clientToBan.emit('roomDeparture', { + roomId: data.roomId, + type: 'kick', + }); + } } } } diff --git a/backend/code/src/rooms/rooms.service.ts b/backend/code/src/rooms/rooms.service.ts index c4ee26f..df3b356 100644 --- a/backend/code/src/rooms/rooms.service.ts +++ b/backend/code/src/rooms/rooms.service.ts @@ -49,6 +49,22 @@ export class RoomsService { HttpStatus.FORBIDDEN, ); } + // check if already there is an existing dm room + const dmRoom = await this.prisma.room.findFirst({ + where: { + type: 'dm', + members: { + every: { + userId: { + in: [roomOwnerId, secondMember], + }, + }, + }, + }, + }); + if (dmRoom) { + return new RoomDataDto(dmRoom); + } } const room = await this.prisma.room.create({ @@ -609,7 +625,7 @@ export class RoomsService { return roomsData; } - async getDms(userId: string, offset: number, limit: number) { + async getDMs(userId: string, offset: number, limit: number) { const rooms = await this.prisma.room.findMany({ skip: offset, take: limit, @@ -627,21 +643,60 @@ export class RoomsService { ownerId: true, members: { select: { - userId: true, + user: { + select: { + userId: true, + firstName: true, + lastName: true, + avatar: true, + }, + }, }, }, }, }); - return rooms.map((room) => { - const secondMember = room.members.find( - (member) => member.userId !== userId, - ); - return { - id: room.id, - name: secondMember.userId, - type: room.type, - ownerId: room.ownerId, - }; - }); + + type DMsData = { + id: string; + name: string; + last_message: { + createdAt: Date; + content: string; + } | null; + secondMemberId: string; + avatar: PICTURE; + }; + const dmsData: DMsData[] = await Promise.all( + rooms.map(async (room) => { + const secondMember = room.members.find( + (member) => member.user.userId !== userId, + ); + const last_message = await this.prisma.message.findFirst({ + where: { + roomId: room.id, + }, + orderBy: { + createdAt: 'desc', + }, + select: { + content: true, + createdAt: true, + }, + }); + const avatar: PICTURE = { + thumbnail: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_48,w_48/${secondMember.user.avatar}`, + medium: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_72,w_72/${secondMember.user.avatar}`, + large: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_128,w_128/${secondMember.user.avatar}`, + }; + return { + id: room.id, + name: secondMember.user.firstName + ' ' + secondMember.user.lastName, + secondMemberId: secondMember.user.userId, + last_message, + avatar, + }; + }), + ); + return dmsData; } } From 8c4a89da0baadb1e9cfbdcf0729953b7abb61519 Mon Sep 17 00:00:00 2001 From: Saad eddyne El abdari Date: Sun, 5 Nov 2023 16:54:00 +0100 Subject: [PATCH 6/7] Revert "add: online presence, handle ban unban and join room in sockets" This reverts commit 939a323b70d0db307e3e1320d752062625a2d8ed. --- backend/code/prisma/dbml/schema.dbml | 1 - .../migration.sql | 56 ---------- backend/code/prisma/schema.prisma | 1 - backend/code/src/gateways/gateways.gateway.ts | 103 ++---------------- backend/code/src/rooms/rooms.service.ts | 81 +++----------- 5 files changed, 22 insertions(+), 220 deletions(-) delete mode 100644 backend/code/prisma/migrations/20231105142742_add_onlien_status_to_user/migration.sql diff --git a/backend/code/prisma/dbml/schema.dbml b/backend/code/prisma/dbml/schema.dbml index 3681745..c876410 100644 --- a/backend/code/prisma/dbml/schema.dbml +++ b/backend/code/prisma/dbml/schema.dbml @@ -11,7 +11,6 @@ Table users { refreshedHash String intraId String [unique] profileFinished Boolean [not null, default: false] - online Boolean [not null, default: false] Username String [unique] firstName String lastName String diff --git a/backend/code/prisma/migrations/20231105142742_add_onlien_status_to_user/migration.sql b/backend/code/prisma/migrations/20231105142742_add_onlien_status_to_user/migration.sql deleted file mode 100644 index 5c46879..0000000 --- a/backend/code/prisma/migrations/20231105142742_add_onlien_status_to_user/migration.sql +++ /dev/null @@ -1,56 +0,0 @@ --- DropForeignKey -ALTER TABLE "blocked_friends" DROP CONSTRAINT "blocked_friends_blocked_by_id_fkey"; - --- DropForeignKey -ALTER TABLE "blocked_friends" DROP CONSTRAINT "blocked_friends_blocked_id_fkey"; - --- DropForeignKey -ALTER TABLE "friends" DROP CONSTRAINT "friends_fromId_fkey"; - --- DropForeignKey -ALTER TABLE "friends" DROP CONSTRAINT "friends_toId_fkey"; - --- DropForeignKey -ALTER TABLE "matches" DROP CONSTRAINT "matches_participant1Id_fkey"; - --- DropForeignKey -ALTER TABLE "matches" DROP CONSTRAINT "matches_participant2Id_fkey"; - --- DropForeignKey -ALTER TABLE "messages" DROP CONSTRAINT "messages_authorId_fkey"; - --- DropForeignKey -ALTER TABLE "room_members" DROP CONSTRAINT "room_members_userId_fkey"; - --- DropForeignKey -ALTER TABLE "rooms" DROP CONSTRAINT "rooms_ownerId_fkey"; - --- AlterTable -ALTER TABLE "users" ADD COLUMN "online" BOOLEAN NOT NULL DEFAULT false; - --- AddForeignKey -ALTER TABLE "friends" ADD CONSTRAINT "friends_fromId_fkey" FOREIGN KEY ("fromId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "friends" ADD CONSTRAINT "friends_toId_fkey" FOREIGN KEY ("toId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "blocked_friends" ADD CONSTRAINT "blocked_friends_blocked_by_id_fkey" FOREIGN KEY ("blocked_by_id") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "blocked_friends" ADD CONSTRAINT "blocked_friends_blocked_id_fkey" FOREIGN KEY ("blocked_id") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "matches" ADD CONSTRAINT "matches_participant1Id_fkey" FOREIGN KEY ("participant1Id") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "matches" ADD CONSTRAINT "matches_participant2Id_fkey" FOREIGN KEY ("participant2Id") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "messages" ADD CONSTRAINT "messages_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "rooms" ADD CONSTRAINT "rooms_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "room_members" ADD CONSTRAINT "room_members_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/backend/code/prisma/schema.prisma b/backend/code/prisma/schema.prisma index 6e324cd..bba7188 100644 --- a/backend/code/prisma/schema.prisma +++ b/backend/code/prisma/schema.prisma @@ -24,7 +24,6 @@ model User { refreshedHash String? intraId String? @unique profileFinished Boolean @default(false) - online Boolean @default(false) Username String? @unique firstName String? diff --git a/backend/code/src/gateways/gateways.gateway.ts b/backend/code/src/gateways/gateways.gateway.ts index 6a8adc3..4100951 100644 --- a/backend/code/src/gateways/gateways.gateway.ts +++ b/backend/code/src/gateways/gateways.gateway.ts @@ -1,7 +1,6 @@ import { MessageBody, OnGatewayConnection, - OnGatewayDisconnect, SubscribeMessage, WebSocketGateway, WebSocketServer, @@ -12,22 +11,20 @@ import {} from '@nestjs/platform-socket.io'; import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'; import { PrismaService } from 'src/prisma/prisma.service'; import { Game } from 'src/game/game'; - @WebSocketGateway(3004, { cors: { origin: ['http://localhost:3001'], }, transports: ['websocket'], }) -export class Gateways implements OnGatewayConnection, OnGatewayDisconnect { +export class Gateways implements OnGatewayConnection { constructor( private prisma: PrismaService, private readonly eventEmitter: EventEmitter2, ) {} - @WebSocketServer() private server: Server; private games_map = new Map(); - async handleConnection(client: Socket) { + handleConnection(client: Socket) { const userId = client.data.user.sub; const rooms = this.prisma.roomMember.findMany({ where: { @@ -48,48 +45,10 @@ export class Gateways implements OnGatewayConnection, OnGatewayDisconnect { console.log(`Romm:${room.room.id}`); }); }); - client.join(`User:${userId}`); - - await this.prisma.user.update({ - where: { - userId, - }, - data: { - online: true, - }, - }); - - const frienduserIds = await this.prisma.friend.findMany({ - where: { - OR: [ - { - fromId: userId, - }, - { - toId: userId, - }, - ], - }, - select: { - fromId: true, - toId: true, - }, - }); - - const friendIds = frienduserIds - .map((friend) => (friend.toId === userId ? friend.fromId : friend.toId)) - .filter((id) => this.server.sockets.adapter.rooms.get(`User:${id}`)?.size); - - client.emit('onlineFriends', friendIds); - - this.server.emit('friendOnline', userId); + client.join(`notif:${userId}`); } - async handleDisconnect(client: Socket) { - const userId = client.data.user.sub; - - this.server.emit('friendOffline', userId); - } + @WebSocketServer() private server: Server; @OnEvent('sendMessages') sendMessage(message: MessageFormatDto) { @@ -99,7 +58,7 @@ export class Gateways implements OnGatewayConnection, OnGatewayDisconnect { @OnEvent('addFriendNotif') sendFriendReq(notif: any) { - const channellname: string = `User:${notif.recipientId}`; + const channellname: string = `notif:${notif.recipientId}`; this.server.to(channellname).emit('message', notif); } @@ -131,49 +90,9 @@ export class Gateways implements OnGatewayConnection, OnGatewayDisconnect { this.games_map.delete(data.gameid); } - @SubscribeMessage('joinRoom') - async handleJoinRoomEvent(client: Socket, data: any) { - const member = await this.prisma.roomMember.findFirst({ - where: { - userId: client.data.user.sub, - roomId: data.roomId, - }, - }); - if (member) { - client.join(`Romm:${data.roomId}`); - } - } - - @SubscribeMessage('unban') - async handleUnbanEvent(client: Socket, data: any) { - const owner = await this.prisma.roomMember.findFirst({ - where: { - userId: client.data.user.sub, - roomId: data.roomId, - }, - }); - if (!owner || owner.is_admin === false) { - return; - } - const member = await this.prisma.roomMember.findFirst({ - where: { - userId: data.memberId, - roomId: data.roomId, - }, - }); - if (member) { - const banedClientSocket = await this.server - .in(`User:${data.memberId}`) - .fetchSockets(); - if (banedClientSocket.length > 0) { - banedClientSocket[0].join(`Romm:${data.roomId}`); - } - } - } - @SubscribeMessage('roomDeparture') async hundleDeparture( - @MessageBody() data: { roomId: string; memberId: string; type: string }, + @MessageBody() data: { roomId: string; memberId: string }, ) { const clients = await this.server.in(`Romm:${data.roomId}`).fetchSockets(); console.log(`Romm:${data.roomId}`); @@ -181,13 +100,9 @@ export class Gateways implements OnGatewayConnection, OnGatewayDisconnect { (client) => client.data.user.sub === data.memberId, ); if (clientToBan) { - clientToBan.leave(`Romm:${data.roomId}`); - if (data?.type === 'kick') { - clientToBan.emit('roomDeparture', { - roomId: data.roomId, - type: 'kick', - }); - } + clientToBan.leave(`Romm:${data.roomId}`); + } else { + console.log('client not found'); } } } diff --git a/backend/code/src/rooms/rooms.service.ts b/backend/code/src/rooms/rooms.service.ts index df3b356..c4ee26f 100644 --- a/backend/code/src/rooms/rooms.service.ts +++ b/backend/code/src/rooms/rooms.service.ts @@ -49,22 +49,6 @@ export class RoomsService { HttpStatus.FORBIDDEN, ); } - // check if already there is an existing dm room - const dmRoom = await this.prisma.room.findFirst({ - where: { - type: 'dm', - members: { - every: { - userId: { - in: [roomOwnerId, secondMember], - }, - }, - }, - }, - }); - if (dmRoom) { - return new RoomDataDto(dmRoom); - } } const room = await this.prisma.room.create({ @@ -625,7 +609,7 @@ export class RoomsService { return roomsData; } - async getDMs(userId: string, offset: number, limit: number) { + async getDms(userId: string, offset: number, limit: number) { const rooms = await this.prisma.room.findMany({ skip: offset, take: limit, @@ -643,60 +627,21 @@ export class RoomsService { ownerId: true, members: { select: { - user: { - select: { - userId: true, - firstName: true, - lastName: true, - avatar: true, - }, - }, + userId: true, }, }, }, }); - - type DMsData = { - id: string; - name: string; - last_message: { - createdAt: Date; - content: string; - } | null; - secondMemberId: string; - avatar: PICTURE; - }; - const dmsData: DMsData[] = await Promise.all( - rooms.map(async (room) => { - const secondMember = room.members.find( - (member) => member.user.userId !== userId, - ); - const last_message = await this.prisma.message.findFirst({ - where: { - roomId: room.id, - }, - orderBy: { - createdAt: 'desc', - }, - select: { - content: true, - createdAt: true, - }, - }); - const avatar: PICTURE = { - thumbnail: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_48,w_48/${secondMember.user.avatar}`, - medium: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_72,w_72/${secondMember.user.avatar}`, - large: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_128,w_128/${secondMember.user.avatar}`, - }; - return { - id: room.id, - name: secondMember.user.firstName + ' ' + secondMember.user.lastName, - secondMemberId: secondMember.user.userId, - last_message, - avatar, - }; - }), - ); - return dmsData; + return rooms.map((room) => { + const secondMember = room.members.find( + (member) => member.userId !== userId, + ); + return { + id: room.id, + name: secondMember.userId, + type: room.type, + ownerId: room.ownerId, + }; + }); } } From f07fd4da07260fba72acfb14b1672cc9b9816112 Mon Sep 17 00:00:00 2001 From: Saad eddyne El abdari Date: Sun, 5 Nov 2023 16:53:05 +0100 Subject: [PATCH 7/7] add: online presence, handle ban unban and join room in sockets --- backend/code/prisma/dbml/schema.dbml | 1 + .../migration.sql | 56 ++++++++++ backend/code/prisma/schema.prisma | 1 + backend/code/src/gateways/gateways.gateway.ts | 103 ++++++++++++++++-- backend/code/src/rooms/rooms.service.ts | 81 +++++++++++--- 5 files changed, 220 insertions(+), 22 deletions(-) create mode 100644 backend/code/prisma/migrations/20231105142742_add_onlien_status_to_user/migration.sql diff --git a/backend/code/prisma/dbml/schema.dbml b/backend/code/prisma/dbml/schema.dbml index c876410..3681745 100644 --- a/backend/code/prisma/dbml/schema.dbml +++ b/backend/code/prisma/dbml/schema.dbml @@ -11,6 +11,7 @@ Table users { refreshedHash String intraId String [unique] profileFinished Boolean [not null, default: false] + online Boolean [not null, default: false] Username String [unique] firstName String lastName String diff --git a/backend/code/prisma/migrations/20231105142742_add_onlien_status_to_user/migration.sql b/backend/code/prisma/migrations/20231105142742_add_onlien_status_to_user/migration.sql new file mode 100644 index 0000000..5c46879 --- /dev/null +++ b/backend/code/prisma/migrations/20231105142742_add_onlien_status_to_user/migration.sql @@ -0,0 +1,56 @@ +-- DropForeignKey +ALTER TABLE "blocked_friends" DROP CONSTRAINT "blocked_friends_blocked_by_id_fkey"; + +-- DropForeignKey +ALTER TABLE "blocked_friends" DROP CONSTRAINT "blocked_friends_blocked_id_fkey"; + +-- DropForeignKey +ALTER TABLE "friends" DROP CONSTRAINT "friends_fromId_fkey"; + +-- DropForeignKey +ALTER TABLE "friends" DROP CONSTRAINT "friends_toId_fkey"; + +-- DropForeignKey +ALTER TABLE "matches" DROP CONSTRAINT "matches_participant1Id_fkey"; + +-- DropForeignKey +ALTER TABLE "matches" DROP CONSTRAINT "matches_participant2Id_fkey"; + +-- DropForeignKey +ALTER TABLE "messages" DROP CONSTRAINT "messages_authorId_fkey"; + +-- DropForeignKey +ALTER TABLE "room_members" DROP CONSTRAINT "room_members_userId_fkey"; + +-- DropForeignKey +ALTER TABLE "rooms" DROP CONSTRAINT "rooms_ownerId_fkey"; + +-- AlterTable +ALTER TABLE "users" ADD COLUMN "online" BOOLEAN NOT NULL DEFAULT false; + +-- AddForeignKey +ALTER TABLE "friends" ADD CONSTRAINT "friends_fromId_fkey" FOREIGN KEY ("fromId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "friends" ADD CONSTRAINT "friends_toId_fkey" FOREIGN KEY ("toId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "blocked_friends" ADD CONSTRAINT "blocked_friends_blocked_by_id_fkey" FOREIGN KEY ("blocked_by_id") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "blocked_friends" ADD CONSTRAINT "blocked_friends_blocked_id_fkey" FOREIGN KEY ("blocked_id") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "matches" ADD CONSTRAINT "matches_participant1Id_fkey" FOREIGN KEY ("participant1Id") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "matches" ADD CONSTRAINT "matches_participant2Id_fkey" FOREIGN KEY ("participant2Id") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "messages" ADD CONSTRAINT "messages_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "rooms" ADD CONSTRAINT "rooms_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "room_members" ADD CONSTRAINT "room_members_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("userId") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/backend/code/prisma/schema.prisma b/backend/code/prisma/schema.prisma index bba7188..6e324cd 100644 --- a/backend/code/prisma/schema.prisma +++ b/backend/code/prisma/schema.prisma @@ -24,6 +24,7 @@ model User { refreshedHash String? intraId String? @unique profileFinished Boolean @default(false) + online Boolean @default(false) Username String? @unique firstName String? diff --git a/backend/code/src/gateways/gateways.gateway.ts b/backend/code/src/gateways/gateways.gateway.ts index 4100951..6a8adc3 100644 --- a/backend/code/src/gateways/gateways.gateway.ts +++ b/backend/code/src/gateways/gateways.gateway.ts @@ -1,6 +1,7 @@ import { MessageBody, OnGatewayConnection, + OnGatewayDisconnect, SubscribeMessage, WebSocketGateway, WebSocketServer, @@ -11,20 +12,22 @@ import {} from '@nestjs/platform-socket.io'; import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'; import { PrismaService } from 'src/prisma/prisma.service'; import { Game } from 'src/game/game'; + @WebSocketGateway(3004, { cors: { origin: ['http://localhost:3001'], }, transports: ['websocket'], }) -export class Gateways implements OnGatewayConnection { +export class Gateways implements OnGatewayConnection, OnGatewayDisconnect { constructor( private prisma: PrismaService, private readonly eventEmitter: EventEmitter2, ) {} + @WebSocketServer() private server: Server; private games_map = new Map(); - handleConnection(client: Socket) { + async handleConnection(client: Socket) { const userId = client.data.user.sub; const rooms = this.prisma.roomMember.findMany({ where: { @@ -45,10 +48,48 @@ export class Gateways implements OnGatewayConnection { console.log(`Romm:${room.room.id}`); }); }); - client.join(`notif:${userId}`); + client.join(`User:${userId}`); + + await this.prisma.user.update({ + where: { + userId, + }, + data: { + online: true, + }, + }); + + const frienduserIds = await this.prisma.friend.findMany({ + where: { + OR: [ + { + fromId: userId, + }, + { + toId: userId, + }, + ], + }, + select: { + fromId: true, + toId: true, + }, + }); + + const friendIds = frienduserIds + .map((friend) => (friend.toId === userId ? friend.fromId : friend.toId)) + .filter((id) => this.server.sockets.adapter.rooms.get(`User:${id}`)?.size); + + client.emit('onlineFriends', friendIds); + + this.server.emit('friendOnline', userId); } - @WebSocketServer() private server: Server; + async handleDisconnect(client: Socket) { + const userId = client.data.user.sub; + + this.server.emit('friendOffline', userId); + } @OnEvent('sendMessages') sendMessage(message: MessageFormatDto) { @@ -58,7 +99,7 @@ export class Gateways implements OnGatewayConnection { @OnEvent('addFriendNotif') sendFriendReq(notif: any) { - const channellname: string = `notif:${notif.recipientId}`; + const channellname: string = `User:${notif.recipientId}`; this.server.to(channellname).emit('message', notif); } @@ -90,9 +131,49 @@ export class Gateways implements OnGatewayConnection { this.games_map.delete(data.gameid); } + @SubscribeMessage('joinRoom') + async handleJoinRoomEvent(client: Socket, data: any) { + const member = await this.prisma.roomMember.findFirst({ + where: { + userId: client.data.user.sub, + roomId: data.roomId, + }, + }); + if (member) { + client.join(`Romm:${data.roomId}`); + } + } + + @SubscribeMessage('unban') + async handleUnbanEvent(client: Socket, data: any) { + const owner = await this.prisma.roomMember.findFirst({ + where: { + userId: client.data.user.sub, + roomId: data.roomId, + }, + }); + if (!owner || owner.is_admin === false) { + return; + } + const member = await this.prisma.roomMember.findFirst({ + where: { + userId: data.memberId, + roomId: data.roomId, + }, + }); + if (member) { + const banedClientSocket = await this.server + .in(`User:${data.memberId}`) + .fetchSockets(); + if (banedClientSocket.length > 0) { + banedClientSocket[0].join(`Romm:${data.roomId}`); + } + } + } + @SubscribeMessage('roomDeparture') async hundleDeparture( - @MessageBody() data: { roomId: string; memberId: string }, + @MessageBody() data: { roomId: string; memberId: string; type: string }, ) { const clients = await this.server.in(`Romm:${data.roomId}`).fetchSockets(); console.log(`Romm:${data.roomId}`); @@ -100,9 +181,13 @@ export class Gateways implements OnGatewayConnection { (client) => client.data.user.sub === data.memberId, ); if (clientToBan) { - clientToBan.leave(`Romm:${data.roomId}`); - } else { - console.log('client not found'); + clientToBan.leave(`Romm:${data.roomId}`); + if (data?.type === 'kick') { + clientToBan.emit('roomDeparture', { + roomId: data.roomId, + type: 'kick', + }); + } } } } diff --git a/backend/code/src/rooms/rooms.service.ts b/backend/code/src/rooms/rooms.service.ts index c4ee26f..df3b356 100644 --- a/backend/code/src/rooms/rooms.service.ts +++ b/backend/code/src/rooms/rooms.service.ts @@ -49,6 +49,22 @@ export class RoomsService { HttpStatus.FORBIDDEN, ); } + // check if already there is an existing dm room + const dmRoom = await this.prisma.room.findFirst({ + where: { + type: 'dm', + members: { + every: { + userId: { + in: [roomOwnerId, secondMember], + }, + }, + }, + }, + }); + if (dmRoom) { + return new RoomDataDto(dmRoom); + } } const room = await this.prisma.room.create({ @@ -609,7 +625,7 @@ export class RoomsService { return roomsData; } - async getDms(userId: string, offset: number, limit: number) { + async getDMs(userId: string, offset: number, limit: number) { const rooms = await this.prisma.room.findMany({ skip: offset, take: limit, @@ -627,21 +643,60 @@ export class RoomsService { ownerId: true, members: { select: { - userId: true, + user: { + select: { + userId: true, + firstName: true, + lastName: true, + avatar: true, + }, + }, }, }, }, }); - return rooms.map((room) => { - const secondMember = room.members.find( - (member) => member.userId !== userId, - ); - return { - id: room.id, - name: secondMember.userId, - type: room.type, - ownerId: room.ownerId, - }; - }); + + type DMsData = { + id: string; + name: string; + last_message: { + createdAt: Date; + content: string; + } | null; + secondMemberId: string; + avatar: PICTURE; + }; + const dmsData: DMsData[] = await Promise.all( + rooms.map(async (room) => { + const secondMember = room.members.find( + (member) => member.user.userId !== userId, + ); + const last_message = await this.prisma.message.findFirst({ + where: { + roomId: room.id, + }, + orderBy: { + createdAt: 'desc', + }, + select: { + content: true, + createdAt: true, + }, + }); + const avatar: PICTURE = { + thumbnail: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_48,w_48/${secondMember.user.avatar}`, + medium: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_72,w_72/${secondMember.user.avatar}`, + large: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_128,w_128/${secondMember.user.avatar}`, + }; + return { + id: room.id, + name: secondMember.user.firstName + ' ' + secondMember.user.lastName, + secondMemberId: secondMember.user.userId, + last_message, + avatar, + }; + }), + ); + return dmsData; } }