diff --git a/Makefile b/Makefile index 27e3080f..5b7050eb 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ DOCKERCOMPOSEPATH=./srcs/docker-compose.yml +all : up + #dev allow hot reload. up: @bash ./make_env.sh @@ -11,8 +13,6 @@ start: docker compose -f ${DOCKERCOMPOSEPATH} start docker logs --follow nestjs -all : up - re: down up down: @@ -28,6 +28,10 @@ destroy: db: - docker rm -f postgresql - docker rm -f nestjs + #- docker rm -f redis + #- docker rm -f game_server + #- docker rm -f nginx + #- docker rm -f svelte - docker volume rm -f srcs_data_nest_postgresql docker compose -f ${DOCKERCOMPOSEPATH} up -d --build @make start diff --git a/README.md b/README.md index c952c5ab..300c2d9d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ - ### Pour lancer le docker : - Il faut un fichier .env qu'on ne doit pas push, donc je ne le push pas. @@ -69,22 +68,24 @@ #### chat : - [/] create public room -- [ ] create private room +- [/] create private room - [/] create direct room - [/] chat in room - [/] join public rooms -- [ ] join private rooms -- [ ] join direct rooms +- [/] join private rooms only by invitation +- [/] join direct rooms - [/] see all joignable rooms +- [/] cannot see private rooms - [/] see all my rooms +- [/] invite someone in room - [/] leave room -- [ ] leave direct -- [ ] invite someone in room +- [/] leave direct impossible +- [/] protect room with password +- [/] add, change, and remove password in room - [ ] make admin - [ ] ban - [ ] mute -- [ ] protect room with password -- [ ] bock users +- [ ] block users - [ ] send game invitation - [ ] view user profiles diff --git a/srcs/docker-compose.yml b/srcs/docker-compose.yml index 3d6fa9c2..eadd2da2 100644 --- a/srcs/docker-compose.yml +++ b/srcs/docker-compose.yml @@ -26,7 +26,7 @@ services: volumes: - ./requirements/nestjs/api_back/src:/usr/app/src - ./requirements/nestjs/api_back/test:/usr/app/test/ - - nestjs_photos_volume:/usr/app/src/uploads/avatars + - nestjs_photos_volume:/usr/app/uploads/avatars restart: unless-stopped depends_on: - postgresql diff --git a/srcs/requirements/game_server/game_back/src/server/class/Client.ts b/srcs/requirements/game_server/game_back/src/server/class/Client.ts index 864472ec..67076fd3 100644 --- a/srcs/requirements/game_server/game_back/src/server/class/Client.ts +++ b/srcs/requirements/game_server/game_back/src/server/class/Client.ts @@ -6,6 +6,7 @@ import * as ev from "../../shared_js/class/Event.js" import * as en from "../../shared_js/enums.js" export class Client { + role: en.ClientRole; socket: WebSocket; id: string; // same as "socket.id" isAlive: boolean = true; @@ -23,9 +24,8 @@ export class ClientPlayer extends Client { inputBuffer: ev.EventInput = new ev.EventInput(); lastInputId: number = 0; racket: Racket; - constructor(socket: WebSocket, id: string, racket: Racket) { + constructor(socket: WebSocket, id: string) { super(socket, id); - this.racket = racket; } } diff --git a/srcs/requirements/game_server/game_back/src/server/wsServer.ts b/srcs/requirements/game_server/game_back/src/server/wsServer.ts index 35923df1..8af02ec6 100644 --- a/srcs/requirements/game_server/game_back/src/server/wsServer.ts +++ b/srcs/requirements/game_server/game_back/src/server/wsServer.ts @@ -76,6 +76,8 @@ async function clientAnnounceListener(this: WebSocket, data: string) if (msg.role === en.ClientRole.player) { const announce: ev.ClientAnnouncePlayer = msg; + const player = clientsMap.get(this.id) as ClientPlayer; + player.role = msg.role; const body = { playerOneUsername: announce.username, @@ -106,8 +108,6 @@ async function clientAnnounceListener(this: WebSocket, data: string) clientTerminate(clientsMap.get(this.id)); return; } - - const player = clientsMap.get(this.id) as ClientPlayer; player.matchOptions = announce.matchOptions; player.token = announce.token; player.username = announce.username; @@ -126,13 +126,15 @@ async function clientAnnounceListener(this: WebSocket, data: string) else if (msg.role === en.ClientRole.spectator) { const announce: ev.ClientAnnounceSpectator = msg; + const spectator = clientsMap.get(this.id) as ClientSpectator; + spectator.role = msg.role; + const gameSession = gameSessionsMap.get(announce.gameSessionId); if (!gameSession) { this.send(JSON.stringify( new ev.EventError("invalid gameSessionId") )); clientTerminate(clientsMap.get(this.id)); return; } - const spectator = clientsMap.get(this.id) as ClientSpectator; spectator.gameSession = gameSession; gameSession.spectatorsMap.set(spectator.id, spectator); spectator.socket.once("message", spectatorReadyConfirmationListener); @@ -419,7 +421,7 @@ const pingInterval = setInterval( () => { }, 4200); -export function clientTerminate(client: Client) +export async function clientTerminate(client: Client) { client.socket.terminate(); if (client.gameSession) @@ -439,6 +441,23 @@ export function clientTerminate(client: Client) else if (privateMatchmakingMap.has(client.id)) { privateMatchmakingMap.delete(client.id); } + + if (client.role === en.ClientRole.player) + { + const player = client as ClientPlayer; + console.log("/resetuserstatus " + player.username); + const response = await fetch(c.addressBackEnd + "/game/gameserver/resetuserstatus", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({username: player.username}) + }); + if (!response.ok) { + console.log("/resetuserstatus " + player.username + " failed"); + } + } } diff --git a/srcs/requirements/nestjs/api_back/package-lock.json b/srcs/requirements/nestjs/api_back/package-lock.json index c3e88cfb..e9fa4b5d 100644 --- a/srcs/requirements/nestjs/api_back/package-lock.json +++ b/srcs/requirements/nestjs/api_back/package-lock.json @@ -20,6 +20,7 @@ "@nestjs/websockets": "^9.2.1", "@types/express-session": "^1.17.5", "@types/validator": "^13.7.9", + "bcrypt": "^5.1.0", "class-transformer": "^0.5.1", "class-validator": "^0.13.2", "connect-redis": "^6.1.3", @@ -44,6 +45,7 @@ "@nestjs/cli": "^9.0.0", "@nestjs/schematics": "^9.0.0", "@nestjs/testing": "^9.0.0", + "@types/bcrypt": "^5.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.8", "@types/multer": "^1.4.7", @@ -1455,6 +1457,25 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", + "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, "node_modules/@nestjs/cli": { "version": "9.1.5", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.1.5.tgz", @@ -2156,6 +2177,15 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/bcrypt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", + "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -2772,6 +2802,11 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2823,6 +2858,17 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", @@ -2945,6 +2991,36 @@ "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -3129,6 +3205,19 @@ "node": ">=6.0.0" } }, + "node_modules/bcrypt": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", + "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.10", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -3425,6 +3514,14 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -3643,6 +3740,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -3702,6 +3807,11 @@ "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -3877,6 +3987,11 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -3904,6 +4019,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -4887,6 +5010,28 @@ "node": ">=12" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/fs-monkey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", @@ -4917,6 +5062,25 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/generic-pool": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", @@ -5090,6 +5254,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "node_modules/hexoid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", @@ -5128,6 +5297,18 @@ "node": ">= 0.8" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -6574,7 +6755,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -6610,7 +6790,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, "dependencies": { "semver": "^6.0.0" }, @@ -6625,7 +6804,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -6764,6 +6942,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", + "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -6850,6 +7062,11 @@ "integrity": "sha512-/ujIVxthRs+7q6hsdjHMaj8hRG9NuWmwrz+JdRwZ14jdFoKSkm+vDsCbF9PLpnSqjaWQJuTmVtcWHNLr+vrOFw==", "dev": true }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, "node_modules/node-emoji": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", @@ -6890,6 +7107,20 @@ "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", "dev": true }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -6911,6 +7142,17 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "node_modules/oauth": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", @@ -8155,7 +8397,6 @@ "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -8306,8 +8547,7 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sisteransi": { "version": "1.0.5", @@ -8630,6 +8870,33 @@ "node": ">=6" } }, + "node_modules/tar": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^4.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -9530,6 +9797,14 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/windows-release": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", @@ -10831,6 +11106,22 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "@mapbox/node-pre-gyp": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", + "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "requires": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + } + }, "@nestjs/cli": { "version": "9.1.5", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.1.5.tgz", @@ -11331,6 +11622,15 @@ "@babel/types": "^7.3.0" } }, + "@types/bcrypt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", + "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -11858,6 +12158,11 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -11893,6 +12198,14 @@ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "devOptional": true }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, "ajv": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", @@ -11975,6 +12288,32 @@ "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -12111,6 +12450,15 @@ "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" }, + "bcrypt": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", + "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.10", + "node-addon-api": "^5.0.0" + } + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -12320,6 +12668,11 @@ "readdirp": "~3.6.0" } }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -12482,6 +12835,11 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -12529,6 +12887,11 @@ "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -12659,6 +13022,11 @@ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -12676,6 +13044,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, + "detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -13439,6 +13812,24 @@ "universalify": "^2.0.0" } }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } + } + }, "fs-monkey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", @@ -13462,6 +13853,22 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, "generic-pool": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", @@ -13581,6 +13988,11 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "hexoid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", @@ -13610,6 +14022,15 @@ "toidentifier": "1.0.1" } }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -14694,7 +15115,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -14718,7 +15138,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, "requires": { "semver": "^6.0.0" }, @@ -14726,8 +15145,7 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -14829,6 +15247,33 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" }, + "minipass": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", + "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } + } + }, "mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -14906,6 +15351,11 @@ "integrity": "sha512-/ujIVxthRs+7q6hsdjHMaj8hRG9NuWmwrz+JdRwZ14jdFoKSkm+vDsCbF9PLpnSqjaWQJuTmVtcWHNLr+vrOFw==", "dev": true }, + "node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, "node-emoji": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", @@ -14935,6 +15385,14 @@ "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", "dev": true }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -14950,6 +15408,17 @@ "path-key": "^3.0.0" } }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "oauth": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", @@ -15848,7 +16317,6 @@ "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -15973,8 +16441,7 @@ "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "sisteransi": { "version": "1.0.5", @@ -16223,6 +16690,26 @@ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true }, + "tar": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^4.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, "terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -16784,6 +17271,14 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "windows-release": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", diff --git a/srcs/requirements/nestjs/api_back/package.json b/srcs/requirements/nestjs/api_back/package.json index 4e8c4666..2c8a47da 100644 --- a/srcs/requirements/nestjs/api_back/package.json +++ b/srcs/requirements/nestjs/api_back/package.json @@ -32,6 +32,7 @@ "@nestjs/websockets": "^9.2.1", "@types/express-session": "^1.17.5", "@types/validator": "^13.7.9", + "bcrypt": "^5.1.0", "class-transformer": "^0.5.1", "class-validator": "^0.13.2", "connect-redis": "^6.1.3", @@ -56,6 +57,7 @@ "@nestjs/cli": "^9.0.0", "@nestjs/schematics": "^9.0.0", "@nestjs/testing": "^9.0.0", + "@types/bcrypt": "^5.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.8", "@types/multer": "^1.4.7", diff --git a/srcs/requirements/nestjs/api_back/src/auth/42/authentication.controller.ts b/srcs/requirements/nestjs/api_back/src/auth/42/authentication.controller.ts index 44de4932..b255592f 100644 --- a/srcs/requirements/nestjs/api_back/src/auth/42/authentication.controller.ts +++ b/srcs/requirements/nestjs/api_back/src/auth/42/authentication.controller.ts @@ -5,6 +5,7 @@ import { Response } from 'express'; import { TwoFaDto } from './dto/2fa.dto'; import { UsersService } from 'src/users/users.service'; import { User } from 'src/users/entities/user.entity'; +import { STATUS } from 'src/common/constants/constants'; @Controller('auth') export class AuthenticationController { @@ -36,6 +37,7 @@ export class AuthenticationController { console.log('On redirige'); const user : User = request.user if (user.isEnabledTwoFactorAuth === false || user.isTwoFactorAuthenticated === true){ + this.userService.updateStatus(user.id, STATUS.CONNECTED) console.log('ON VA VERS PROFILE'); return response.status(200).redirect('http://' + process.env.WEBSITE_HOST + ':' + process.env.WEBSITE_PORT + '/#/profile'); } @@ -51,7 +53,7 @@ export class AuthenticationController { @UseGuards(AuthenticateGuard) logout(@Req() request, @Res() response, @Next() next) { this.userService.setIsTwoFactorAuthenticatedWhenLogout(request.user.id); - this.userService.updateStatus(request.user.id, 'disconnected'); + this.userService.updateStatus(request.user.id, STATUS.DISCONNECTED); request.logout(function(err) { if (err) { return next(err); } response.redirect('/'); @@ -83,6 +85,7 @@ export class AuthenticationController { throw new UnauthorizedException('Wrong Code.'); await this.userService.authenticateUserWith2FA(request.user.id); console.log('ON REDIRIGE'); + this.userService.updateStatus(user.id, STATUS.CONNECTED) return response.status(200).redirect('http://' + process.env.WEBSITE_HOST + ':' + process.env.WEBSITE_PORT + '/#/profile'); } } diff --git a/srcs/requirements/nestjs/api_back/src/chat/chat.controller.ts b/srcs/requirements/nestjs/api_back/src/chat/chat.controller.ts index 12c8a9b1..a2570d8d 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/chat.controller.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/chat.controller.ts @@ -9,6 +9,7 @@ import { setCurrentRoomDto } from './dto/setCurrentRoom.dto'; import { ChatGateway } from './chat.gateway'; import { socketDto } from './dto/socket.dto'; import { Chatroom } from './entities/chatroom.entity'; +import { printCaller } from './dev/dev_utils'; @Controller('chat') export class ChatController { @@ -18,6 +19,22 @@ export class ChatController { private chatGateway: ChatGateway, ) {} + private format_room(room) + { + let new_room: roomDto = { + name: room.name, + type: room.type, + protection: room.protection, + }; + if (room.owner) + new_room.owner = room.owner; + if (room.users) + new_room.users = room.users; + if (room.allowed) + new_room.allowed = room.allowed; + return new_room; + } + // don't allow '+' because it's used in direct rooms name private allowed_chars = '-#!?_'; private escape_chars(str) @@ -31,14 +48,24 @@ export class ChatController { @Get('myrooms') async getMyRooms(@Req() req, @Res() res): Promise { - console.log("- in getMyRooms controller"); + printCaller("- in "); - let fields = ["name", "type", "users"]; + let fields = ["name", "type", "users", "protection", "allowed_users"]; const rooms = await this.chatService.getMyRooms(req.user.username, fields); - res.status(HttpStatus.OK).json({ rooms: rooms }); + rooms.forEach(room => { + if (room.protection) + { + if (room.allowed_users.includes(req.user.username)) + room.allowed = true; + else + room.allowed = false; + } + }); - console.log("- out getMyRooms controller"); + const ret_rooms = rooms.map(room => this.format_room(room)); + res.status(HttpStatus.OK).json({ rooms: ret_rooms }); + printCaller("- out "); } @UseGuards(AuthenticateGuard) @@ -46,13 +73,13 @@ export class ChatController { @Get('allrooms') async getAllRooms(@Req() req, @Res() res): Promise { - console.log("- in getAllRooms controller"); + printCaller("- in "); const rooms: roomDto[] = await this.chatService.getAllOtherRoomsAndUsers(req.user.username) - console.log("--- rooms:", rooms); - res.status(HttpStatus.OK).json({ rooms: rooms }); - console.log("- out getAllRooms controller"); + const ret_rooms = rooms.map(room => this.format_room(room)); + res.status(HttpStatus.OK).json({ rooms: ret_rooms }); + printCaller("- out "); } @UseGuards(AuthenticateGuard) @@ -60,12 +87,11 @@ export class ChatController { @Get('current') async setCurrentRoom(@Body() setCurrentRoomDto: setCurrentRoomDto, @Req() req, @Res() res): Promise { - console.log("- in setCurrentRoom controller"); + printCaller("- in "); const response = await this.chatService.setCurrentRoom(req.user.username, setCurrentRoomDto.name); res.status(HttpStatus.OK).json({ message: response }); - - console.log("- out setCurrentRoom controller"); + printCaller("- out "); } @UseGuards(AuthenticateGuard) @@ -73,11 +99,10 @@ export class ChatController { @Get('allowedchars') async allowedChars(@Res() res): Promise { - console.log("- in allowedChars controller"); + printCaller("- in "); res.status(HttpStatus.OK).json({ chars: this.allowed_chars }); - - console.log("- out allowedChars controller"); + printCaller("- out "); } @UseGuards(AuthenticateGuard) @@ -85,21 +110,46 @@ export class ChatController { @Post('create') async createRoom(@Body() room: roomDto, @Req() req, @Res() res): Promise { - console.log("- in createRoom controller"); + printCaller("- in "); + // check chars in room name let chars = this.escape_chars(this.allowed_chars); let regex_base = `[a-zA-Z0-9\\s${chars}]`; let test_regex = new RegExp(`^${regex_base}+$`); if (test_regex.test(room.name) === false) { let forbidden_chars = room.name.replace(new RegExp(regex_base, "g"), ""); - throw new HttpException(`Your room name can not contains these characters : ${forbidden_chars}`, HttpStatus.UNPROCESSABLE_ENTITY); + printCaller(`throw error: error: true, code: 'FORBIDDEN_CHARACTERS', message: 'Your room name can not contains these characters : ${forbidden_chars}'`); + throw new HttpException({ error: true, code: 'FORBIDDEN_CHARACTERS', message: `Your room name can not contains these characters : ${forbidden_chars}` }, HttpStatus.UNPROCESSABLE_ENTITY); } - await this.chatService.addUserToNewRoom(req.user.username, room); - res.status(HttpStatus.OK).json({ room: room }); + // check for password protection + if (typeof room.protection === 'undefined') + room.protection = false; + else if (room.protection === true) + { + if (!room.password || room.password.length === 0) + { + printCaller(`throw error: error: true, code: 'PASSWORD_INVALID', message: 'your password is invalid'`); + throw new HttpException({ error: true, code: 'PASSWORD_INVALID', message: `your password is invalid` }, HttpStatus.UNPROCESSABLE_ENTITY); + } + } - console.log("- out createRoom controller"); + room.users = [req.user.username]; + await this.chatService.addUserToNewRoom(req.user.username, room); + + if (room.protection) + { + let message = `${req.user.username} changed the password`; + room.allowed_users = [req.user.username]; + await this.chatService.setPassword(req.user.username, message, room); + let socket: socketDto = this.chatGateway.sockets.get(req.user.username); + await socket.to(socket.room).emit('message', "SERVER", message); + } + + const ret_room = this.format_room(room); + res.status(HttpStatus.OK).json({ room: ret_room }); + printCaller("- out "); } @UseGuards(AuthenticateGuard) @@ -107,26 +157,45 @@ export class ChatController { @Post('join') async joinRoom(@Body() room: roomDto, @Req() req, @Res() res): Promise { - console.log("- in joinRoom controller"); + printCaller("- in "); let response = ""; - if (room.type === 'direct') - throw new HttpException(`cannot join a direct messages room`, HttpStatus.CONFLICT); - else if (room.type === 'user') + if (room.type === 'user') { room.type = 'direct'; room.users = [room.name, req.user.username]; room.name += ` + ${req.user.username}`; + room.owner = req.user.username; await this.chatService.addUserToNewRoom(req.user.username, room); } else - await this.chatService.addUserToRoom(req.user.username, room.name); + { + let fields = ["name", "type", "users", "messages", "owner", "protection"]; + const room_db = await this.chatService.getRoomByName(room.name, fields); + if (room_db.type === 'direct') + { + console.log("throw error: error: true, code: 'JOIN_DIRECT_FORBIDDEN', message: 'cannot join a direct messages room'"); + throw new HttpException({ error: true, code: 'JOIN_DIRECT_FORBIDDEN', message: `cannot join a direct messages room` }, HttpStatus.CONFLICT); + } + if (room_db.type === 'private') + { + console.log("throw error: error: true, code: 'JOIN_PRIVATE_FORBIDDEN', message: 'cannot join a private room'"); + throw new HttpException({ error: true, code: 'JOIN_PRIVATE_FORBIDDEN', message: `cannot join a private room` }, HttpStatus.CONFLICT); + } + if (room_db.users.includes(req.user.username)) + { + console.log("throw error: error: true, code: 'ALREADY_JOIN', message: 'your have already joined this room'"); + throw new HttpException({ error: true, code: 'ALREADY_JOIN', message: `your have already joined this room` }, HttpStatus.CONFLICT); + } + room = await this.chatService.addUserToRoom(req.user.username, room.name); + } let socket: socketDto = this.chatGateway.sockets.get(req.user.username); await this.chatService.socketJoinRoom(socket, room.name); - res.status(HttpStatus.OK).json({ room: room }); - console.log("- out joinRoom controller"); + const ret_room = this.format_room(room); + res.status(HttpStatus.OK).json({ room: ret_room }); + printCaller("- out "); } @UseGuards(AuthenticateGuard) @@ -134,23 +203,156 @@ export class ChatController { @Post('change') async changeRoom(@Body() room: roomDto, @Req() req, @Res() res): Promise { - console.log("- in changeRoom controller"); + printCaller("- in "); - const response = await this.chatService.setCurrentRoom(req.user.username, room.name); + let fields = ["protection", "allowed_users"]; + const room_db = await this.chatService.getRoomByName(room.name, fields); + + if (room_db.protection === true) + { + if (!room_db.allowed_users.includes(req.user.username)) + { + console.log("throw error: error: true, code: 'NEED_AUTHENTICATE', message: 'You didn't provide the password for this room'"); + throw new HttpException({ error: true, code: 'NEED_AUTHENTICATE', message: `You didn't provide the password for this room` }, HttpStatus.UNAUTHORIZED); + } + } + + await this.chatService.setCurrentRoom(req.user.username, room.name); let socket: socketDto = this.chatGateway.sockets.get(req.user.username); await this.chatService.socketChangeRoom(socket, room.name); - res.status(HttpStatus.OK).json({ room: room }); - console.log("- out changeRoom controller"); + const ret_room = this.format_room(room); + res.status(HttpStatus.OK).json({ room: ret_room }); + printCaller("- out "); } @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) - @Post('leave') - async leaveRoom(@Body() body) + @Post('passwordauth') + async passwordAuthentication(@Body() room: roomDto, @Req() req, @Res() res): Promise { - console.log("- in leaveRoom controller"); - console.log("- out leaveRoom controller"); + printCaller("- in "); + + let fields = ["protection", "allowed_users"]; + const room_db = await this.chatService.getRoomByName(room.name, fields); + if (room_db.protection === true) + { + if (!room.password) + { + console.log("throw error: error: true, code: 'PASSWORD_MISSING', message: 'this room is protected, you need to provide a password'"); + throw new HttpException({ error: true, code: 'PASSWORD_MISSING', message: `this room is protected, you need to provide a password` }, HttpStatus.UNAUTHORIZED); + } + if (!room_db.allowed_users.includes(req.user.username)) + await this.chatService.setPasswordValidation(req.user.username, room); + } + + const ret_room = this.format_room(room); + res.status(HttpStatus.OK).json({ room: ret_room }); + printCaller("- out "); + } + + @UseGuards(AuthenticateGuard) + @UseGuards(TwoFactorGuard) + @Post('changepassword') + async changePassword(@Body('room') room: roomDto, @Body('old_password') old_password: string, @Req() req, @Res() res): Promise + { + printCaller("- in "); + + let message = `${req.user.username} changed the password`; + room.allowed_users = [req.user.username]; + room.protection = true; + await this.chatService.setPassword(req.user.username, message, room, old_password); + + // inform other connected users + let socket: socketDto = this.chatGateway.sockets.get(req.user.username); + await socket.to(socket.room).emit('message', "SERVER", message); + + const ret_room = this.format_room(room); + res.status(HttpStatus.OK).json({ room: ret_room }); + printCaller("- out "); + } + + @UseGuards(AuthenticateGuard) + @UseGuards(TwoFactorGuard) + @Post('addpassword') + async addPassword(@Body() room: roomDto, @Req() req, @Res() res): Promise + { + printCaller("- in "); + + let message = `${req.user.username} added a password`; + room.allowed_users = [req.user.username]; + room.protection = true; + await this.chatService.setPassword(req.user.username, message, room); + + // inform other connected users + let socket: socketDto = this.chatGateway.sockets.get(req.user.username); + await socket.to(socket.room).emit('message', "SERVER", message); + + const ret_room = this.format_room(room); + res.status(HttpStatus.OK).json({ room: ret_room }); + printCaller("- out "); + } + + @UseGuards(AuthenticateGuard) + @UseGuards(TwoFactorGuard) + @Delete('removepassword') + async removePassword(@Body() room: roomDto, @Req() req, @Res() res): Promise + { + printCaller("- in "); + + let message = `${req.user.username} removed a new password`; + room.allowed_users = []; + room.protection = false; + await this.chatService.setPassword(req.user.username, message, room); + + // inform other connected users + let socket: socketDto = this.chatGateway.sockets.get(req.user.username); + await socket.to(socket.room).emit('message', "SERVER", message); + + const ret_room = this.format_room(room); + res.status(HttpStatus.OK).json({ room: ret_room }); + printCaller("- out "); + } + + @UseGuards(AuthenticateGuard) + @UseGuards(TwoFactorGuard) + @Post('invite') + async inviteUser(@Body('username') username: string, @Req() req, @Res() res): Promise + { + printCaller("- in "); + + let current_room_name = await this.chatService.getCurrentRoomName(req.user.username); + let room = await this.chatService.addUserToRoom(username, current_room_name); + let message = `${username} joined the room`; + await this.chatService.addMessageToRoom(current_room_name, "SERVER", message); + + const ret_room = this.format_room(room); + res.status(HttpStatus.OK).json({ room: ret_room }); + printCaller("- out "); + } + + @UseGuards(AuthenticateGuard) + @UseGuards(TwoFactorGuard) + @Delete('leave') + async leaveRoom(@Req() req, @Res() res): Promise + { + printCaller("- in "); + + const room_name = await this.chatService.getCurrentRoomName(req.user.username); + let response = await this.chatService.removeUserFromRoom(req.user.username, room_name); + + // set current room to nothing + await this.chatService.setCurrentRoom(req.user.username, ""); + + // leaving message + let message = `${req.user.username} left the room`; + await this.chatService.addMessageToRoom(room_name, "SERVER", message); + let socket: socketDto = this.chatGateway.sockets.get(req.user.username); + await socket.to(socket.room).emit('message', "SERVER", message); + await socket.leave(socket.room); + + res.status(HttpStatus.OK).json({ message: response }); + printCaller("- out "); } @UseGuards(AuthenticateGuard) @@ -158,12 +360,11 @@ export class ChatController { @Get('messages') async getMessages(@Req() req, @Res() res): Promise { - console.log("- in getMessages controller"); + printCaller("- in "); const messages = await this.chatService.getMessagesFromCurrentRoom(req.user.username); res.status(HttpStatus.OK).json({ messages: messages }); - - console.log("- out getMessages controller"); + printCaller("- out "); } @UseGuards(AuthenticateGuard) @@ -171,28 +372,33 @@ export class ChatController { @Get('roomusers') async getRoomUsers(@Req() req, @Res() res): Promise { - console.log("- in getRoomUsers controller"); + printCaller("- in "); const room_name = await this.chatService.getCurrentRoomName(req.user.username); const room = await this.chatService.getRoomByName(room_name); const users = room.users; - res.status(HttpStatus.OK).json({ users: users }); - console.log("- out getRoomUsers controller"); + let index = users.indexOf(req.user.username); + if (index > -1) + { + users.splice(index, 1); + } + + res.status(HttpStatus.OK).json({ users: users }); + printCaller("- out "); } @UseGuards(AuthenticateGuard) @UseGuards(TwoFactorGuard) - @Delete('removeuser') - async removeUser(@Req() req, @Res() res): Promise + @Get('users') + async getAllUsers(@Req() req, @Res() res): Promise { - console.log("- in removeUser controller"); + printCaller("- in "); const room_name = await this.chatService.getCurrentRoomName(req.user.username); - let response = await this.chatService.removeUserFromRoom(req.user.username, room_name); - res.status(HttpStatus.OK).json({ message: response }); - - console.log("- out removeUser controller"); + const users = await this.chatService.getAllUsersNotInRoom(room_name); + res.status(HttpStatus.OK).json({ users: users }); + printCaller("- out "); } } diff --git a/srcs/requirements/nestjs/api_back/src/chat/chat.gateway.ts b/srcs/requirements/nestjs/api_back/src/chat/chat.gateway.ts index b3963f1b..1892ce4d 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/chat.gateway.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/chat.gateway.ts @@ -34,12 +34,10 @@ implements OnGatewayConnection, OnGatewayDisconnect async joinRoom(@ConnectedSocket() socket: socketDto, @MessageBody() room_name: string): Promise { console.log('- in joinRoom gateway'); - socket.leave(socket.room); - socket.join(room_name); - socket.room = room_name; - let message = `${socket.username} has join the room`; - await socket.to(socket.room).emit('message', "SERVER", message); - await this.chatService.addMessageToRoom(room_name, "SERVER", message); + + await this.chatService.socketJoinRoom(socket, room_name) + + console.log('- out joinRoom gateway'); } @SubscribeMessage('change') @@ -47,6 +45,7 @@ implements OnGatewayConnection, OnGatewayDisconnect { console.log('- in changeRoom gateway'); await this.chatService.socketChangeRoom(socket, room_name); + console.log('- out changeRoom gateway'); } @SubscribeMessage('message') @@ -54,6 +53,7 @@ implements OnGatewayConnection, OnGatewayDisconnect { console.log('- in handleMessage gateway'); await this.chatService.socketIncommingMessage(socket, message); + console.log('- out handleMessage gateway'); } } diff --git a/srcs/requirements/nestjs/api_back/src/chat/chat.service.ts b/srcs/requirements/nestjs/api_back/src/chat/chat.service.ts index a59720db..e094920f 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/chat.service.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/chat.service.ts @@ -7,6 +7,8 @@ import { InjectRepository } from '@nestjs/typeorm'; import { roomDto } from './dto/room.dto'; import { messagesDto } from './dto/messages.dto'; import { socketDto } from './dto/socket.dto'; +import * as bcrypt from 'bcrypt'; +import { printCaller } from './dev/dev_utils'; @Injectable() @@ -31,7 +33,7 @@ export class ChatService { async getMyRooms(username: string, fieldsToReturn: string[] = null): Promise { - console.log("-- in getMyRooms service"); + printCaller("-- in "); const queryBuilder = this.chatroomRepository .createQueryBuilder('chatroom') @@ -44,55 +46,52 @@ export class ChatService { } const rooms = await queryBuilder.getMany(); - console.log("--- rooms:", rooms); - console.log("-- out getMyRooms service"); + printCaller("-- out "); return rooms; } async getMyDirects(username: string): Promise { - console.log("-- in getAllNotMyRooms service"); + printCaller("-- in "); const my_rooms = await this.getMyRooms(username); const directs = my_rooms.filter(room => room.type === 'direct'); - console.log("-- out getAllNotMyRooms service"); + printCaller("-- out "); return directs; } async getAllRooms(): Promise { - console.log("-- in getAllRooms service"); + printCaller("-- in "); const rooms = await this.chatroomRepository .createQueryBuilder('chatroom') .getMany(); - console.log("--- rooms:", rooms); - console.log("-- out getAllRooms service"); + printCaller("-- out "); return rooms; } async getAllNotMyRooms(username: string): Promise { - console.log("-- in getAllNotMyRooms service"); + printCaller("-- in "); const user_db = await this.getUserByName(username); const rooms = await this.chatroomRepository .createQueryBuilder('chatroom') - .where('chatroom.type != :type', { type: 'private' }) + .where('chatroom.type NOT IN (:...type)', { type: ['private', 'direct'] }) .andWhere('chatroom.users NOT LIKE :user_name', { user_name: `%${username}%` }) .getMany(); - console.log("--- rooms:", rooms); - console.log("-- out getAllNotMyRooms service"); + printCaller("-- out "); return rooms; } async getAllOtherRoomsAndUsers(username: string): Promise { - console.log("-- in getAllOtherRoomsAndUsers service"); + printCaller("-- in "); const all_rooms = await this.getAllNotMyRooms(username); const all_users = await this.getAllUsersNotMyRooms(username); @@ -101,24 +100,25 @@ export class ChatService { return { name: room.name, type: room.type, + protection: room.protection, }; }); let users = all_users.map(user => { return { name: user.username, type: "user", + protection: false, }; }); - let rooms = row_rooms.concat(users); - console.log("--- rooms:", rooms); + let rooms: roomDto[] = row_rooms.concat(users); - console.log("-- in getAllOtherRoomsAndUsers service"); + printCaller("-- out "); return rooms; } async getMessagesFromCurrentRoom(username: string): Promise { - console.log("-- in getMessagesFromCurrentRoom service"); + printCaller("-- in "); const user_db = await this.getUserByName(username); const currentRoom = await this.getRoomByName(user_db.currentRoom); @@ -126,45 +126,50 @@ export class ChatService { if (currentRoom) messages = currentRoom.messages; - console.log("-- out getMessagesFromCurrentRoom service"); + printCaller("-- out "); return messages; } async getCurrentRoomName(username: string): Promise { - console.log("-- in getCurrentRoomName service"); + printCaller("-- in "); const user_db = await this.getUserByName(username); - console.log("-- out getCurrentRoomName service"); + printCaller("-- out "); return user_db.currentRoom; } - async getRoomByName(room_name: string): Promise + async getRoomByName(room_name: string, fieldsToReturn: string[] = null): Promise { - console.log("-- in getRoomByName service"); + printCaller("-- in "); - const room = await this.chatroomRepository + const queryBuilder = this.chatroomRepository .createQueryBuilder('chatroom') - .where('chatroom.name = :name', { name: room_name }) - .getOne(); - console.log("--- room:", room); + .where('chatroom.name = :name', { name: room_name }); - console.log("-- out getRoomByName service"); + if (fieldsToReturn) + { + let fields = fieldsToReturn.map(field => `chatroom.${field}`); + queryBuilder.select(fields); + } + + const room = await queryBuilder.getOne(); + + printCaller("-- out "); return room; } async getRoomById(id: number): Promise { - console.log("-- in getRoomById service"); + printCaller("-- in "); const room = await this.chatroomRepository .createQueryBuilder('chatroom') .where('chatroom.id = :id', { id: id }) .getOne(); - console.log("--- room:", room); - console.log("-- out getRoomById service"); + printCaller("-- out "); return room; } @@ -174,70 +179,146 @@ export class ChatService { async setCurrentRoom(username: string, room_name: string): Promise { - console.log("-- in setCurrentRoom service"); + printCaller("-- in "); const user_db = await this.getUserByName(username); user_db.currentRoom = room_name; - this.userRepository.save(user_db); - - console.log("-- out setCurrentRoom service"); + await this.userRepository.save(user_db); + + printCaller("-- out "); return `room "${room_name}" is now current room`; } + async setPasswordValidation(username: string, room: roomDto): Promise + { + printCaller("-- in "); + + const room_db = await this.getRoomByName(room.name); + const is_match = await bcrypt.compare(room.password, room_db.hash); + if (!is_match) + { + printCaller(`throw error: error: true, code: 'BAD_PASSWORD', message: 'bad password'`); + throw new HttpException({ error: true, code: 'BAD_PASSWORD', message: `bad password` }, HttpStatus.BAD_REQUEST); + } + + room_db.allowed_users.push(username); + printCaller("-- out "); + await this.chatroomRepository.save(room_db); + } + + async setPassword(username: string, message: string, room: roomDto, old_password?: string): Promise + { + printCaller("-- in "); + + if (room.type === 'direct') + { + console.log("throw error: error: true, code: 'DIRECT_PASSWORD_FORBIDDEN', message: 'you cannot set a password in a direct message room'"); + throw new HttpException({ error: true, code: 'DIRECT_PASSWORD_FORBIDDEN', message: `you cannot set a password in a direct message room` }, HttpStatus.FORBIDDEN); + } + + const current_room = await this.getRoomByName(room.name); + + if (!room.password) + { + console.log("throw error: error: true, code: 'NO_PASSWORD', message: 'this room has no password protection'"); + throw new HttpException({ error: true, code: 'NO_PASSWORD', message: `this room has no password protection` }, HttpStatus.FORBIDDEN); + } + if (current_room.protection) + { + if (room.protection && !old_password) + { + console.log("throw error: error: true, code: 'MISSING_OLD_PASSWORD', message: 'you need to provide the old password to set a new one'"); + throw new HttpException({ error: true, code: 'MISSING_OLD_PASSWORD', message: `you need to provide the old password to set a new one` }, HttpStatus.FORBIDDEN); + } + if (old_password) + { + const is_match = await bcrypt.compare(old_password, current_room.hash); + if (!is_match) + { + printCaller(`throw error: error: true, code: 'BAD_PASSWORD', message: 'you provided a bad password'`); + throw new HttpException({ error: true, code: 'BAD_PASSWORD', message: `you provided a bad password` }, HttpStatus.BAD_REQUEST); + } + } + } + + const saltOrRounds = 10; + const password = room.password; + let hash: string; + if (room.protection) + hash = await bcrypt.hash(password, saltOrRounds); + + // add password to chatroom + current_room.allowed_users = room.allowed_users; + current_room.protection = room.protection; + if (room.protection) + current_room.hash = hash; + else + delete current_room.hash; + current_room.messages.push({ name: "SERVER", message: message }); + await this.chatroomRepository.save(current_room); + + printCaller("-- out "); + } + /* ADDERS ************************************************* */ async addUserToNewRoom(username: string, room: roomDto): Promise { - console.log("-- in addUserToNewRoom service"); + printCaller("-- in "); const find_room = await this.getRoomByName(room.name); if (find_room) - throw new HttpException(`This room name already exist`, HttpStatus.CONFLICT); + { + console.log("throw error: error: true, code: 'ROOM_CONFLICT', message: 'This room name already exist'"); + throw new HttpException({ error: true, code: 'ROOM_CONFLICT', message: `This room name already exist` }, HttpStatus.CONFLICT); + } // create chatroom - const newChatroom = new Chatroom(); + let newChatroom = new Chatroom(); newChatroom.name = room.name; newChatroom.type = room.type; newChatroom.owner = username; - newChatroom.users = [username]; - if (room.type === 'direct') - newChatroom.users = room.users; - newChatroom.messages = [{ name: "SERVER", message: `creation of room ${room.name}` }]; + newChatroom.users = room.users; + newChatroom.allowed_users = []; + newChatroom.protection = false; + newChatroom.messages = + [ + { name: "SERVER", message: `creation of room ${room.name}` }, + { name: "SERVER", message: `${room.users[0]} joined the room` }, + ]; await this.chatroomRepository.save(newChatroom); - - console.log("-- out addUserToNewRoom service"); + printCaller("-- out "); } - async addUserToRoom(username: string, room_name: string): Promise + async addUserToRoom(username: string, room_name: string): Promise { - console.log("-- in addUserToRoom service"); + printCaller("-- in "); const room = await this.getRoomByName(room_name); - if (room.users.includes(username)) - throw new HttpException(`your have already joined this room`, HttpStatus.CONFLICT); // update room with new user room.users.push(username); - this.chatroomRepository.save(room); + await this.chatroomRepository.save(room); - console.log("-- out addUserToRoom service"); + printCaller("-- out "); + return room; } async addMessageToRoom(room_name: string, username: string, message: string): Promise { - console.log("-- in addMessageToRoom service"); + printCaller("-- in "); const my_room = await this.getRoomByName(room_name); - let chat_message = { + let chat_message: messagesDto = { name: username, message: message, }; my_room.messages.push(chat_message); - this.chatroomRepository.save(my_room); - console.log("-- out addMessageToRoom service"); + printCaller("-- out "); + await this.chatroomRepository.save(my_room); } @@ -246,21 +327,26 @@ export class ChatService { async removeUserFromRoom(username: string, room_name: string): Promise { - console.log("-- in removeUserFromRoom service"); + printCaller("-- in "); const room = await this.getRoomByName(room_name); if (!room.users.includes(username)) - throw new HttpException(`your are not in this room`, HttpStatus.CONFLICT); + { + console.log("throw error: error: true, code: 'USER_NOT_FOUND', message: 'your are not in this room'"); + throw new HttpException({ error: true, code: 'USER_NOT_FOUND', message: `your are not in this room` }, HttpStatus.NOT_FOUND); + } + if (room.type === "direct") + { + console.log("throw error: error: true, code: 'LEAVE_DIRECY_FORBIDDEN', message: 'you cannot leave a direct messages conversation'"); + throw new HttpException({ error: true, code: 'LEAVE_DIRECY_FORBIDDEN', message: `you cannot leave a direct messages conversation` }, HttpStatus.FORBIDDEN); + } // delete user from room room.users.push(username); room.users = room.users.filter(name => name !== username); - this.chatroomRepository.save(room); + await this.chatroomRepository.save(room); - // set current room to nothing - await this.setCurrentRoom(username, ""); - - console.log("-- out removeUserFromRoom service"); + printCaller("-- out "); return "successfully leaving room"; } @@ -270,21 +356,20 @@ export class ChatService { async getUserByName(username: string): Promise { - console.log("-- in getUserByName service"); + printCaller("-- in "); const user = await this.userRepository .createQueryBuilder('user') .where('user.username = :name', { name: username }) .getOne(); - console.log("--- user:", user); - console.log("-- out getUserByName service"); + printCaller("-- out "); return user; } async getAllUsersNotMyRooms(username: string): Promise { - console.log("-- in getAllUsersNotMyRooms service"); + printCaller("-- in "); const directs = await this.getMyDirects(username); @@ -301,9 +386,25 @@ export class ChatService { .createQueryBuilder('user') .where('user.username NOT IN (:...usernames)', { usernames: usernames }) .getMany(); - console.log("--- users:", users); - console.log("-- out getAllUsersNotMyRooms service"); + printCaller("-- out "); + return users; + } + + async getAllUsersNotInRoom(current_room_name: string): Promise + { + printCaller("-- in "); + + // get all users in current room + const current_room = await this.getRoomByName(current_room_name); + const usernames = current_room.users; + + const users = await this.userRepository + .createQueryBuilder('user') + .where('user.username NOT IN (:...usernames)', { usernames: usernames }) + .getMany(); + + printCaller("-- out "); return users; } @@ -313,38 +414,38 @@ export class ChatService { async socketIncommingMessage(socket: socketDto, message: string): Promise { - console.log("-- in handleSocketIncommingMessage service"); + printCaller("-- in "); socket.to(socket.room).emit('message', socket.username, message); let room_name = await this.getCurrentRoomName(socket.username); await this.addMessageToRoom(room_name, socket.username, message); - console.log("-- out handleSocketIncommingMessage service"); + printCaller("-- out "); } async socketChangeRoom(socket: socketDto, room_name: string): Promise { - console.log('-- in socketChangeRoom service'); + printCaller("-- in "); socket.leave(socket.room); socket.join(room_name); socket.room = room_name; - console.log('-- out socketChangeRoom service'); + printCaller("-- out "); } async socketJoinRoom(socket: socketDto, room_name: string): Promise { - console.log('- in socketJoinRoom service'); + printCaller("-- in "); socket.leave(socket.room); socket.join(room_name); socket.room = room_name; - let message = `${socket.username} has join the room`; + let message = `${socket.username} joined the room`; await socket.to(socket.room).emit('message', "SERVER", message); await this.addMessageToRoom(room_name, "SERVER", message); - console.log('- out socketJoinRoom service'); + printCaller("-- out "); } } diff --git a/srcs/requirements/nestjs/api_back/src/chat/dev/dev_utils.ts b/srcs/requirements/nestjs/api_back/src/chat/dev/dev_utils.ts new file mode 100644 index 00000000..309c1e34 --- /dev/null +++ b/srcs/requirements/nestjs/api_back/src/chat/dev/dev_utils.ts @@ -0,0 +1,13 @@ +export function printCaller(prefix: string = "") { + try + { + throw new Error(); + } + catch (e) + { + Error.captureStackTrace(e); + let stack = e.stack.split('\n'); + let caller = stack[2].trim(); + console.log(prefix + ' ' + caller); + } +} diff --git a/srcs/requirements/nestjs/api_back/src/chat/dto/room.dto.ts b/srcs/requirements/nestjs/api_back/src/chat/dto/room.dto.ts index 6dcf8099..e3451468 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/dto/room.dto.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/dto/room.dto.ts @@ -1,19 +1,55 @@ -import { IsBoolean, IsEmpty, IsInt, IsIn, IsNotEmpty, IsNumber, IsArray, IsString, IsOptional, IsEnum } from "class-validator"; +import { IsBoolean, IsEmpty, IsInt, IsIn, IsNotEmpty, IsNumber, IsArray, IsString, IsInstance, ValidateNested, IsObject, IsOptional, IsEnum } from "class-validator"; +import { messagesDto } from 'src/chat/dto/messages.dto'; export class roomDto { + @IsNumber() + @IsOptional() + id?: number; + @IsString() @IsNotEmpty() name: string; @IsString() @IsNotEmpty() - @IsIn(["public", "protected", "private", "direct", "user"]) + @IsIn(["public", "private", "direct", "user"]) type: string; + @IsBoolean() + protection: boolean; + + @IsBoolean() + @IsOptional() + allowed?: boolean; + + @IsString() + @IsOptional() + password?: string; + + @IsString() + @IsOptional() + owner?: string; + + @IsString() + @IsOptional() + client_name?: string; + @IsArray() @IsString({ each: true }) @IsOptional() users?: string[]; // usernames + + @IsArray() + @IsString({ each: true }) + @IsOptional() + allowed_users?: string[]; // usernames + + @IsArray() + //@IsInstance(messagesDto, { each: true }) + //@IsObject({ each: true }) + @ValidateNested({ each: true }) + @IsOptional() + messages?: messagesDto[]; } diff --git a/srcs/requirements/nestjs/api_back/src/chat/entities/chatroom.entity.ts b/srcs/requirements/nestjs/api_back/src/chat/entities/chatroom.entity.ts index 89e8ff18..82522e1a 100644 --- a/srcs/requirements/nestjs/api_back/src/chat/entities/chatroom.entity.ts +++ b/srcs/requirements/nestjs/api_back/src/chat/entities/chatroom.entity.ts @@ -18,17 +18,35 @@ export class Chatroom @Column() @IsString() @IsNotEmpty() - @IsIn(["public", "protected", "private", "direct", "user"]) + @IsIn(["public", "private", "direct", "user"]) type: string; + @Column({ default: false }) + @IsBoolean() + protection: boolean = false; + + @Column({ nullable: true }) + @IsBoolean() + @IsOptional() + allowed?: boolean; + + @Column({ nullable: true }) + @IsString() + @IsOptional() + hash?: string; + @Column() owner: string; // username @Column("simple-array") @IsArray() @IsString({ each: true }) - @IsOptional() - users?: string[]; // usernames + users: string[]; // usernames + + @Column("simple-array") + @IsArray() + @IsString({ each: true }) + allowed_users: string[]; // usernames @Column("json") messages: messagesDto[]; diff --git a/srcs/requirements/nestjs/api_back/src/game/game.controller.ts b/srcs/requirements/nestjs/api_back/src/game/game.controller.ts index b393eaf5..57a87978 100644 --- a/srcs/requirements/nestjs/api_back/src/game/game.controller.ts +++ b/srcs/requirements/nestjs/api_back/src/game/game.controller.ts @@ -101,4 +101,9 @@ export class GameController { { return this.gameService.destroySession(token); } + + @Post('gameserver/resetuserstatus') + async resetUserStatus(@Body('username') username){ + this.gameService.resetStatus(username); + } } diff --git a/srcs/requirements/nestjs/api_back/src/game/game.service.ts b/srcs/requirements/nestjs/api_back/src/game/game.service.ts index a6f7cd02..4e6d4805 100644 --- a/srcs/requirements/nestjs/api_back/src/game/game.service.ts +++ b/srcs/requirements/nestjs/api_back/src/game/game.service.ts @@ -92,12 +92,17 @@ export class GameService { } this.userRepository.save(user); } - // if (grantTicketDto.isGameIsWithInvitation === true && user.status !== STATUS.IN_GAME) // WIP: need to fix STATUS.IN_GAME if (grantTicketDto.isGameIsWithInvitation === true) { - const secondUser : Partial = await this.userService.findOne(grantTicketDto.playerTwoUsername) - if (!secondUser || secondUser.username === user.username) - return res.status(HttpStatus.NOT_FOUND).json({message : "User not found OR you want to play with yourself."}); + if (grantTicketDto.playerTwoUsername === user.username) { + return res.status(HttpStatus.BAD_REQUEST).json({message : "You cant play against yourself."}); + } + const secondUser : User = await this.userRepository.createQueryBuilder('user') + .where("user.username = :username", {username : grantTicketDto.playerTwoUsername}) + .getOne(); + if (!secondUser) { + return res.status(HttpStatus.NOT_FOUND).json({message : "Invited user not found"}); + } const encryptedTextToReturn = await this.encryptToken(user.username + '_' + secondUser.username + '_' + grantTicketDto.gameOptions + '_' + grantTicketDto.isGameIsWithInvitation + '_' + new Date()) const tok = this.tokenGameRepository.create(grantTicketDto); @@ -106,9 +111,9 @@ export class GameService { tok.token = encryptedTextToReturn; this.tokenGameRepository.save(tok); this.userService.updateStatus(user.id, STATUS.IN_POOL) + this.userService.updateStatus(secondUser.id, STATUS.IN_POOL) return res.status(HttpStatus.OK).json({ token : encryptedTextToReturn }); } - // else if (grantTicketDto.isGameIsWithInvitation === false && user.status !== STATUS.IN_GAME) { // WIP: need to fix STATUS.IN_GAME else if (grantTicketDto.isGameIsWithInvitation === false) { const encryptedTextToReturn = await this.encryptToken(user.username + '_' + grantTicketDto.gameOptions + '_' + grantTicketDto.isGameIsWithInvitation + '_' + new Date()) @@ -142,13 +147,11 @@ export class GameService { const userOne : User = await this.userRepository.createQueryBuilder('user') .where("user.username = :username", {username : tokenGame.playerOneUsername}) .getOne(); - this.userService.updateStatus(userOne.id, STATUS.IN_GAME) const userTwo : User = await this.userRepository.createQueryBuilder('user') .where("user.username = :username", {username : tokenGame.playerTwoUsername}) .getOne(); this.deleteToken(userOne) this.deleteToken(userTwo) - this.userService.updateStatus(userTwo.id, STATUS.IN_GAME) } return true; } @@ -168,7 +171,6 @@ export class GameService { const user : User = await this.userRepository.createQueryBuilder('user') .where("user.username = :username", {username : tokenGame.playerOneUsername}) .getOne(); - this.userService.updateStatus(user.id, STATUS.IN_GAME) this.deleteToken(user) return true; } @@ -198,8 +200,10 @@ export class GameService { async declineInvitation(user : User, token : string, @Res() res : Response) { - if (user.status !== STATUS.CONNECTED) - return res.status(HttpStatus.FORBIDDEN).json({message : "You must not be in game to decline an invitation"}); + /* Luke: le check de user.status n'est pas fonctionnel avec l'implémentation des invitations dans le front. + Ça me semble dispensable, je désactive donc pour le moment plutôt que de refaire l'implémentation front. */ + // if (user.status !== STATUS.CONNECTED) + // return res.status(HttpStatus.FORBIDDEN).json({message : "You must not be in game to decline an invitation"}); console.log("On décline l'invitation") const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokengame') .andWhere('tokengame.playerTwoUsername = :playerTwoUsername', {playerTwoUsername : user.username}) @@ -234,8 +238,10 @@ export class GameService { async acceptInvitation(user : User, token : string, @Res() res : Response) { - if (user.status !== STATUS.CONNECTED) - return res.status(HttpStatus.FORBIDDEN).send("") + /* Luke: le check de user.status n'est pas fonctionnel avec l'implémentation des invitations dans le front. + Ça me semble dispensable, je désactive donc pour le moment plutôt que de refaire l'implémentation front. */ + // if (user.status !== STATUS.CONNECTED) + // return res.status(HttpStatus.FORBIDDEN).send("") const tokenGame = await this.tokenGameRepository.createQueryBuilder('tokenGame') .andWhere('tokenGame.playerTwoUsername = :playerTwoUsername', {playerTwoUsername : user.username}) .andWhere('tokenGame.token = :token', {token : token}) @@ -259,6 +265,14 @@ export class GameService { this.gameRepository.save(game); if (!game) return HttpStatus.INTERNAL_SERVER_ERROR + const playerOne : User = await this.userRepository.createQueryBuilder('user') + .where("user.username = :username", {username : creategameDto.playerOneUsername}) + .getOne(); + const playerTwo : User = await this.userRepository.createQueryBuilder('user') + .where("user.username = :username", {username : creategameDto.playerTwoUsername}) + .getOne(); + this.userService.updateStatus(playerOne.id, STATUS.IN_GAME) + this.userService.updateStatus(playerTwo.id, STATUS.IN_GAME) console.log("200 retourné pour la création de partie") return HttpStatus.OK } @@ -280,6 +294,8 @@ export class GameService { const playerTwo = await this.userRepository.findOneBy({username : game.playerTwoUsername}) if (!playerOne || !playerTwo) return new HttpException("Internal Server Error. Impossible to update the database", HttpStatus.INTERNAL_SERVER_ERROR); + this.userService.updateStatus(playerOne.id, STATUS.CONNECTED); + this.userService.updateStatus(playerTwo.id, STATUS.CONNECTED); if (game.playerOneUsernameResult === game.playerTwoUsernameResult) { this.userService.incrementDraws(playerOne.id) @@ -296,9 +312,14 @@ export class GameService { this.userService.incrementVictories(playerOne.id) this.userService.incrementDefeats(playerTwo.id) } - this.userService.updateStatus(playerOne.id, STATUS.CONNECTED) - this.userService.updateStatus(playerTwo.id, STATUS.CONNECTED) return HttpStatus.OK } + + async resetStatus(username : string){ + const user : User = await this.userRepository.findOneBy({username : username}) + if (!user) + return HttpStatus.NOT_FOUND; + this.userService.updateStatus(user.id, STATUS.CONNECTED); + } } diff --git a/srcs/requirements/svelte/api_front/public/global.css b/srcs/requirements/svelte/api_front/public/global.css index 440eb9e6..b07ffb9c 100644 --- a/srcs/requirements/svelte/api_front/public/global.css +++ b/srcs/requirements/svelte/api_front/public/global.css @@ -1,4 +1,5 @@ + html, body { position: relative; width: 100%; @@ -12,14 +13,23 @@ body { box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; /* tmp? */ - background: bisque; - - /* overflow-x: hidden; */ - /* This seems to have fixed my pages that are too long */ - /* but now i can't scroll anywhere ... */ - /* overflow-y: hidden; */ + background-color: #333; + display: flex; + flex-direction: column; } +@font-face { + font-family: "Bit5x3"; + src: + url("./fonts/Bit5x3.woff2") format("woff2"), + local("Bit5x3"), + url("./fonts/Bit5x3.woff") format("woff"); + font-weight: normal; + font-style: normal; + font-display: swap; +} + + a { color: rgb(0,100,200); text-decoration: none; @@ -33,6 +43,13 @@ a:visited { color: rgb(0,80,160); } +.background-pages { + background-color: #333; + font-family: "Bit5x3"; + font-size: 2vw; + color: white; +} + label { display: block; } diff --git a/srcs/requirements/svelte/api_front/public/img/BACKGROUND.png b/srcs/requirements/svelte/api_front/public/img/BACKGROUND.png new file mode 100644 index 00000000..7f39521f Binary files /dev/null and b/srcs/requirements/svelte/api_front/public/img/BACKGROUND.png differ diff --git a/srcs/requirements/svelte/api_front/public/img/SPLASH_PAGE_BACKGROUND.png b/srcs/requirements/svelte/api_front/public/img/SPLASH_PAGE_BACKGROUND.png new file mode 100644 index 00000000..90136036 Binary files /dev/null and b/srcs/requirements/svelte/api_front/public/img/SPLASH_PAGE_BACKGROUND.png differ diff --git a/srcs/requirements/svelte/api_front/public/img/cartoon_potato1.png b/srcs/requirements/svelte/api_front/public/img/cartoon_potato1.png deleted file mode 100644 index 2db693d7..00000000 Binary files a/srcs/requirements/svelte/api_front/public/img/cartoon_potato1.png and /dev/null differ diff --git a/srcs/requirements/svelte/api_front/public/img/cartoon_potato2.jpg b/srcs/requirements/svelte/api_front/public/img/cartoon_potato2.jpg deleted file mode 100644 index 41ba6dba..00000000 Binary files a/srcs/requirements/svelte/api_front/public/img/cartoon_potato2.jpg and /dev/null differ diff --git a/srcs/requirements/svelte/api_front/public/img/default.png b/srcs/requirements/svelte/api_front/public/img/default.png new file mode 100644 index 00000000..1dfcbae0 Binary files /dev/null and b/srcs/requirements/svelte/api_front/public/img/default.png differ diff --git a/srcs/requirements/svelte/api_front/public/img/default_user_icon.png b/srcs/requirements/svelte/api_front/public/img/default_user_icon.png deleted file mode 100644 index fee7a53d..00000000 Binary files a/srcs/requirements/svelte/api_front/public/img/default_user_icon.png and /dev/null differ diff --git a/srcs/requirements/svelte/api_front/public/img/logo_potato.png b/srcs/requirements/svelte/api_front/public/img/logo_potato.png new file mode 100644 index 00000000..3a02aebc Binary files /dev/null and b/srcs/requirements/svelte/api_front/public/img/logo_potato.png differ diff --git a/srcs/requirements/svelte/api_front/public/img/potato_only.png b/srcs/requirements/svelte/api_front/public/img/potato_only.png new file mode 100644 index 00000000..b2f1bb12 Binary files /dev/null and b/srcs/requirements/svelte/api_front/public/img/potato_only.png differ diff --git a/srcs/requirements/svelte/api_front/public/img/tmp_mario_banner.png b/srcs/requirements/svelte/api_front/public/img/tmp_mario_banner.png deleted file mode 100644 index af7cf499..00000000 Binary files a/srcs/requirements/svelte/api_front/public/img/tmp_mario_banner.png and /dev/null differ diff --git a/srcs/requirements/svelte/api_front/public/img/wave-haikei.svg b/srcs/requirements/svelte/api_front/public/img/wave-haikei.svg deleted file mode 100644 index 4a002190..00000000 --- a/srcs/requirements/svelte/api_front/public/img/wave-haikei.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/srcs/requirements/svelte/api_front/src/pages/NotFound.svelte b/srcs/requirements/svelte/api_front/src/pages/NotFound.svelte index f34342e6..da6072d3 100644 --- a/srcs/requirements/svelte/api_front/src/pages/NotFound.svelte +++ b/srcs/requirements/svelte/api_front/src/pages/NotFound.svelte @@ -1,10 +1,26 @@ -

We are sorry!

-

This isn't a url that we use.

-

Go home you're drunk.

- -

Take me home →

-
\ No newline at end of file + +
+
+

We are sorry!

+

This isn't a url that we use.

+

Go home you're drunk.

+
+
+ + \ No newline at end of file diff --git a/srcs/requirements/svelte/api_front/src/pages/SplashPage.svelte b/srcs/requirements/svelte/api_front/src/pages/SplashPage.svelte index d2497cb5..f3dccddf 100644 --- a/srcs/requirements/svelte/api_front/src/pages/SplashPage.svelte +++ b/srcs/requirements/svelte/api_front/src/pages/SplashPage.svelte @@ -9,22 +9,8 @@ user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`) .then((resp) => resp.json()) - // i mean i could do a failed to load user or some shit, maybe with a .catch or something? but atm why bother - - console.log('User var'); - console.log(user); - // if (user && user.statusCode && user.statusCode === 403) { - // console.log('user not logged in') - // } - // if (user && user.username) { - // console.log('we have a user'); - // } - - // user === undefined doesn't work cuz we declared user in the scope of onMount - // if (user === undefined) { if (user && user.statusCode && user.statusCode === 403) { console.log('on mount no user, returned status code 403 so logging out of userStore') - // userLogout(); // which i think should delete any previous local storage } }); @@ -34,8 +20,6 @@ console.log('you are now logged in'); } -// i could prolly put this in it's own compoent, i seem to use it in several places... or maybe just some JS? like no need for html -// we could .then( () => replace('/') ) need the func so TS compatible... const logout = async() => { await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/auth/logout`, { method: 'POST', @@ -46,108 +30,71 @@ -
-

Potato Pong

- -

-
Welcome to
-
Potato Pong
-

-
- - - - - +
+
+ {#if user && user.username} + + + {:else} + + {/if} +
+
diff --git a/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte b/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte index d2d39d21..87933cc8 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte +++ b/srcs/requirements/svelte/api_front/src/pages/game/Game.svelte @@ -4,11 +4,13 @@ import { fade, fly } from 'svelte/transition'; import Header from '../../pieces/Header.svelte'; - import { fetchAvatar } from "../../pieces/utils"; + import { fetchUser, fetchAllUsers, fetchAvatar } from "../../pieces/utils"; import * as pong from "./client/pong"; import { gameState } from "./client/ws"; + import { invited_username } from '../../pieces/store_invitation'; + //user's stuff let user; let allUsers; @@ -36,11 +38,21 @@ const watchMatchStartIntervalRate = 111; onMount( async() => { - user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`) - .then( x => x.json() ); - allUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/all`) - .then( x => x.json() ); + user = await fetchUser(); + allUsers = await fetchAllUsers(); + + if (!user) { + showError = true; + errorMessage = "User load failed"; + return; + } + options.playerOneUsername = user.username; + if ($invited_username) { + options.isSomeoneIsInvited = true; + options.playerTwoUsername = $invited_username; + invited_username.set(""); + } }) onDestroy( async() => { @@ -268,29 +280,38 @@
game options -
+ +
-
+ Multiples balls + + +
+ Moving walls + +
-

sound :

- - - - -
-
- - + sound : + +
+ + + {#if options.isSomeoneIsInvited} {/if} @@ -339,11 +360,16 @@ } #game_page { margin: 0; - padding: 20px; background-color: #222425; position: relative; width: 100%; - height: 100%; + height: auto; + display: flex; + flex-direction: column; + flex-grow: 1; +} +#game_option { + margin-top: 20px; } #canvas_container { margin-top: 20px; diff --git a/srcs/requirements/svelte/api_front/src/pages/game/GameSpectator.svelte b/srcs/requirements/svelte/api_front/src/pages/game/GameSpectator.svelte index 557b35ae..dfa4d63b 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/GameSpectator.svelte +++ b/srcs/requirements/svelte/api_front/src/pages/game/GameSpectator.svelte @@ -6,15 +6,11 @@ import Header from '../../pieces/Header.svelte'; import MatchListElem from "../../pieces/MatchListElem.svelte"; import type { Match } from "../../pieces/Match"; - import { fetchAvatar } from "../../pieces/utils"; + import { fetchUser, fetchAllUsers, fetchAvatar } from "../../pieces/utils"; import * as pongSpectator from "./client/pongSpectator"; import { gameState } from "./client/ws"; - //user's stuff - let user; - let allUsers; - let playerOneAvatar; let playerTwoAvatar; @@ -30,13 +26,7 @@ const watchGameStateIntervalRate = 142; onMount( async() => { - user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`) - .then( x => x.json() ); - allUsers = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/all`) - .then( x => x.json() ); - const responseForMatchList = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`) - const jsonForMatchList = await responseForMatchList.json(); - matchList = jsonForMatchList; + matchList = await fetchMatchList(); }) onDestroy( async() => { @@ -82,12 +72,25 @@ async function resetPage() { hiddenGame = true; pongSpectator.destroy(); - fetchMatchList(); + matchList = await fetchMatchList(); }; - async function fetchMatchList() { - matchList = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`) - .then( x => x.json() ); + async function fetchMatchList() + { + return fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/game/match/all`) + .then((response) => { + if (!response.ok) { + throw new Error("All matchs not retrieved"); + } + return response.json(); + }) + .then((body) => { + return body; + }) + .catch((error) => { + console.log("catch fetchMatchList: ", error); + return []; + }); }; @@ -131,13 +134,19 @@
options +
-

sound :

- - - - + sound : + +
+
{#if matchList.length !== 0} diff --git a/srcs/requirements/svelte/api_front/src/pages/game/Ranking.svelte b/srcs/requirements/svelte/api_front/src/pages/game/Ranking.svelte index 5585b051..af39892f 100644 --- a/srcs/requirements/svelte/api_front/src/pages/game/Ranking.svelte +++ b/srcs/requirements/svelte/api_front/src/pages/game/Ranking.svelte @@ -28,7 +28,7 @@

- +
@@ -59,6 +59,7 @@
+
diff --git a/srcs/requirements/svelte/api_front/src/pages/profile/ProfileFriends.svelte b/srcs/requirements/svelte/api_front/src/pages/profile/ProfileFriends.svelte index 71c780c6..8dc48d37 100644 --- a/srcs/requirements/svelte/api_front/src/pages/profile/ProfileFriends.svelte +++ b/srcs/requirements/svelte/api_front/src/pages/profile/ProfileFriends.svelte @@ -24,22 +24,10 @@ onMount( async() => { - // DO I ACTUALLY NEED TO ON MOUNT ALL THIS STUFF? - // ALSO I COULD JUST USE THE FUNCITONS I MADE... - - - // yea no idea what - // i mean do i fetch user? i will for now user = await fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`) .then( (x) => x.json() ); fetchAll(); - // ok this shit works! - // const interval = setInterval(() => { - // fetchAll(); - // }, 1000); - - // return () => clearInterval(interval); }); const fetchAll = async() => { @@ -234,6 +222,7 @@ +
@@ -353,7 +342,7 @@
- + diff --git a/srcs/requirements/svelte/api_front/src/pieces/chat/Chat.svelte b/srcs/requirements/svelte/api_front/src/pieces/chat/Chat.svelte index 06b2ef15..ad691fc8 100644 --- a/srcs/requirements/svelte/api_front/src/pieces/chat/Chat.svelte +++ b/srcs/requirements/svelte/api_front/src/pieces/chat/Chat.svelte @@ -1,7 +1,7 @@ - diff --git a/srcs/requirements/svelte/api_front/src/pieces/chat/Interface_chat.ts b/srcs/requirements/svelte/api_front/src/pieces/chat/Interface_chat.ts deleted file mode 100644 index ca9b761c..00000000 --- a/srcs/requirements/svelte/api_front/src/pieces/chat/Interface_chat.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface Room -{ - name: string; - type: "public" | "protected" | "private" | "direct" | "user"; - users?: string[]; -} diff --git a/srcs/requirements/svelte/api_front/src/pieces/chat/Layout_create.svelte b/srcs/requirements/svelte/api_front/src/pieces/chat/Layout_create.svelte index a68b70f6..99030f01 100644 --- a/srcs/requirements/svelte/api_front/src/pieces/chat/Layout_create.svelte +++ b/srcs/requirements/svelte/api_front/src/pieces/chat/Layout_create.svelte @@ -3,6 +3,7 @@ import { msgs, layout, allowed_chars } from './Store_chat'; import { change_room, create_room } from './Request_rooms'; import { onMount } from 'svelte'; + import { FetchResponse } from './Types_chat'; import Button from './Element_button.svelte'; import Warning from './Element_warning.svelte'; @@ -13,18 +14,16 @@ onMount(async() => { let response = await fetch('/api/v2/chat/allowedchars'); let data = await response.json(); - console.log("data:", data); allowed_chars = data.chars; //regex = new RegExp(`^[a-zA-Z0-9\\s${allowed_chars}]+$`); }); let room_name: string; let room_type: string; + let is_protected = false; let room_password: string; - let response = { - status: 0, - message: "", - } + let response: FetchResponse; + let show_error = false; async function handleSubmit(evt) { @@ -36,12 +35,18 @@ let room = { name: room_name, type: room_type, + protection: is_protected, }; + if (is_protected === true) + room.password = room_password; + // send the new room response = await create_room(room); // go to room - if (response.status === 200) + if (response.status >= 300 || response.error) + show_error = response.error; + else await change_room(response.room); } @@ -67,7 +72,7 @@
- {#if response.status >= 300} + {#if show_error} {/if} @@ -82,21 +87,19 @@ -
diff --git a/srcs/requirements/svelte/api_front/src/pieces/store_invitation.ts b/srcs/requirements/svelte/api_front/src/pieces/store_invitation.ts new file mode 100644 index 00000000..7dbb0fab --- /dev/null +++ b/srcs/requirements/svelte/api_front/src/pieces/store_invitation.ts @@ -0,0 +1,4 @@ + +import { writable } from 'svelte/store'; + +export const invited_username = writable(""); diff --git a/srcs/requirements/svelte/api_front/src/pieces/utils.ts b/srcs/requirements/svelte/api_front/src/pieces/utils.ts index 4003d8ca..76125f46 100644 --- a/srcs/requirements/svelte/api_front/src/pieces/utils.ts +++ b/srcs/requirements/svelte/api_front/src/pieces/utils.ts @@ -13,5 +13,42 @@ export async function fetchAvatar(username: string) }) .catch((error) => { console.log("catch fetchAvatar: ", error); + return `http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/img/default.png`; + }); +} + +export async function fetchUser() +{ + return fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user`) + .then((response) => { + if (!response.ok) { + throw new Error("User not retrieved"); + } + return response.json(); + }) + .then((body) => { + return body; + }) + .catch((error) => { + console.log("catch fetchUser: ", error); + return null; + }); +} + +export async function fetchAllUsers() +{ + return fetch(`http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}/api/v2/user/all`) + .then((response) => { + if (!response.ok) { + throw new Error("All Users not retrieved"); + } + return response.json(); + }) + .then((body) => { + return body; + }) + .catch((error) => { + console.log("catch fetchAllUsers: ", error); + return []; }); }