This commit is contained in:
Me
2023-01-15 20:28:26 +01:00
59 changed files with 1948 additions and 706 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -76,6 +76,8 @@ async function clientAnnounceListener(this: WebSocket, data: string)
if (msg.role === en.ClientRole.player)
{
const announce: ev.ClientAnnouncePlayer = <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 = <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");
}
}
}

View File

@@ -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",

View File

@@ -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",

View File

@@ -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');
}
}

View File

@@ -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<void>
{
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<void>
{
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<void>
{
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<void>
{
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<void>
{
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<void>
{
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<void>
{
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<void>
{
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<void>
{
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<void>
{
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<void>
{
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<void>
{
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<void>
{
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<void>
{
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<void>
{
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<void>
@Get('users')
async getAllUsers(@Req() req, @Res() res): Promise<void>
{
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 ");
}
}

View File

@@ -34,12 +34,10 @@ implements OnGatewayConnection, OnGatewayDisconnect
async joinRoom(@ConnectedSocket() socket: socketDto, @MessageBody() room_name: string): Promise<void>
{
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');
}
}

View File

@@ -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<Chatroom[]>
{
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<Chatroom[]>
{
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<Chatroom[]>
{
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<Chatroom[]>
{
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<roomDto[]>
{
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<messagesDto[]>
{
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<string>
{
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<Chatroom>
async getRoomByName(room_name: string, fieldsToReturn: string[] = null): Promise<Chatroom>
{
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<Chatroom>
{
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<string>
{
console.log("-- in setCurrentRoom service");
printCaller("-- in ");
const user_db = await this.getUserByName(username);
user_db.currentRoom = room_name;
this.userRepository.save(user_db);
await this.userRepository.save(user_db);
console.log("-- out setCurrentRoom service");
printCaller("-- out ");
return `room "${room_name}" is now current room`;
}
async setPasswordValidation(username: string, room: roomDto): Promise<void>
{
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<void>
{
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<void>
{
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<void>
async addUserToRoom(username: string, room_name: string): Promise<roomDto>
{
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<void>
{
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<string>
{
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<User>
{
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<User[]>
{
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<User[]>
{
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<void>
{
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<void>
{
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<void>
{
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 ");
}
}

View File

@@ -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);
}
}

View File

@@ -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[];
}

View File

@@ -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[];

View File

@@ -101,4 +101,9 @@ export class GameController {
{
return this.gameService.destroySession(token);
}
@Post('gameserver/resetuserstatus')
async resetUserStatus(@Body('username') username){
this.gameService.resetStatus(username);
}
}

View File

@@ -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<User> = await this.userService.findOne(grantTicketDto.playerTwoUsername)
if (!secondUser || secondUser.username === user.username)
return res.status(HttpStatus.NOT_FOUND).json({message : "User not found OR you want to play with yourself."});
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);
}
}

View File

@@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 675 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 430 KiB

View File

@@ -1 +0,0 @@
<svg id="visual" viewBox="0 0 900 150" width="900" height="150" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"><path d="M0 36L21.5 33.7C43 31.3 86 26.7 128.8 32.5C171.7 38.3 214.3 54.7 257.2 63.5C300 72.3 343 73.7 385.8 65.7C428.7 57.7 471.3 40.3 514.2 30.5C557 20.7 600 18.3 642.8 25.8C685.7 33.3 728.3 50.7 771.2 59.5C814 68.3 857 68.7 878.5 68.8L900 69L900 0L878.5 0C857 0 814 0 771.2 0C728.3 0 685.7 0 642.8 0C600 0 557 0 514.2 0C471.3 0 428.7 0 385.8 0C343 0 300 0 257.2 0C214.3 0 171.7 0 128.8 0C86 0 43 0 21.5 0L0 0Z" fill="#618174" stroke-linecap="round" stroke-linejoin="miter"></path></svg>

Before

Width:  |  Height:  |  Size: 645 B

View File

@@ -1,10 +1,26 @@
<script>
import { link } from "svelte-spa-router";
import Header from '../pieces/Header.svelte';
</script>
<h1>We are sorry!</h1>
<p>This isn't a url that we use.</p>
<p>Go home you're drunk.</p>
<a href="/" use:link>
<h2>Take me home →</h2>
</a>
<div class="background-pages">
<div class="content">
<h1>We are sorry!</h1>
<p>This isn't a url that we use.</p>
<p>Go home you're drunk.</p>
</div>
</div>
<style>
.content {
left: 50%;
position: absolute;
-ms-transform: translateX(-50%);
transform: translateX(-50%);
}
</style>

View File

@@ -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 @@
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<header class="grid-container">
<h1>Potato Pong</h1>
<nav>
<!-- {#if user !== undefined} -->
{#if user && user.username}
<div on:click={ () => (push('/profile')) }>Profile</div>
<div on:click={logout}>Logout</div>
{:else}
<div on:click={login}>Login</div>
{/if}
</nav>
<h2>
<div>Welcome to</div>
<div>Potato Pong</div>
</h2>
</header>
<Canvas/>
<!-- doesn't work :( -->
<!-- <svelte:body style="overflow-y: hidden"/> -->
<div class="container">
<div class="splash-page">
{#if user && user.username}
<button class="button-in" on:click={ () => (push('/profile')) }>Profile</button>
<button class="button-out" on:click={logout}>Logout</button>
{:else}
<button class="button-in" on:click={login}>Login</button>
{/if}
</div>
</div>
<style>
@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;
}
/* :global(body) {
overflow-y: hidden;
} */
header {
/* didn't work... */
overflow-y: hidden;
}
.container {
height: 100%;
width: 100%;
position: relative;
background-image: url('/img/SPLASH_PAGE_BACKGROUND.png');
background-position: center center;
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
}
/* The actually important stuff */
.splash-page {
margin: 0;
position: absolute;
top: 80%;
-ms-transform: translateY(-80%);
transform: translateY(-80%);
left: 50%;
-ms-transform: translateX(-50%);
transform: translateX(-50%);
}
.grid-container{
position: absolute;
left: 0;
top: 0;
.button-in {
background-color: #8c0000;
border-color: #071013;
border-width: 2px;
color: white;
font-family: "Bit5x3";
font-size: x-large;
padding: 10px;
}
box-sizing: border-box;
width: 100%;
height: 100%;
/* max-height: 100%; */
white-space: nowrap;
/* padding-bottom: 0; */
margin-bottom: 0px;
overflow: hidden;
padding: 20px 40px;
margin: 0px;
.button-out {
background-color: #008c8c;
border-color: #071013;
border-width: 2px;
color: white;
font-family: "Bit5x3";
font-size: x-large;
padding: 10px;
}
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: 1fr 1fr 1fr 1fr 1fr;
align-items: center;
}
header h1{
grid-column: 1 / 7;
grid-row: 1;
/* grid-column: span 6; */
/* tmp? */
padding: 20px;
border: 1px solid bisque;
}
header nav{
/* make it a flexbox? */
grid-column: 7 / 13;
grid-row: 1;
justify-self: end;
/* tmp? */
padding: 20px;
border: 1px solid bisque;
}
header h2{
grid-row: 3;
grid-column: 5 / span 4;
justify-self: center;
/* tmp */
border: 1px solid black;
z-index: 3;
}
header h2 div{
font-size: 2em;
/* color: red; */
}
nav div {
display: inline;
color: bisque;
font-weight: bold;
}
nav > div {
padding-left: 1em;
/* didn't quite work, applies to both for some reason */
/* nav:first-child doesn't work either*/
}
nav div:hover{
text-decoration: underline;
cursor: pointer;
}
</style>

View File

@@ -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 @@
<button class="pong_button" on:click={fetchInvitations}>Show invitations</button>
<fieldset in:fly="{{ y: 10, duration: 1000 }}">
<legend>game options</legend>
<div>
<label for="multi_balls">
<input type="checkbox" id="multi_balls" name="multi_balls" bind:checked={options.multi_balls}>
<label for="multi_balls">Multiples balls</label>
</div>
<div>
Multiples balls
</label>
<label for="moving_walls">
<input type="checkbox" id="moving_walls" name="moving_walls" bind:checked={options.moving_walls}>
<label for="moving_walls">Moving walls</label>
</div>
Moving walls
</label>
<div>
<p>sound :</p>
<input type="radio" id="sound_on" name="sound_selector" bind:group={options.sound} value="on">
<label for="sound_on">on</label>
<input type="radio" id="sound_off" name="sound_selector" bind:group={options.sound} value="off">
<label for="sound_off">off</label>
</div>
<div>
<input type="checkbox" id="isSomeoneIsInvited" bind:checked={options.isSomeoneIsInvited}>
<label for="moving_walls">Invite a friend</label>
sound :
<label for="sound_on">
<input type="radio" id="sound_on" name="sound_selector" bind:group={options.sound} value="on">
on
</label>
<label for="sound_off">
<input type="radio" id="sound_off" name="sound_selector" bind:group={options.sound} value="off">
off
</label>
</div>
<label for="invitation_checkbox">
<input type="checkbox" id="invitation_checkbox" bind:checked={options.isSomeoneIsInvited}>
Invite a friend
</label>
{#if options.isSomeoneIsInvited}
<select bind:value={options.playerTwoUsername}>
{#each allUsers as user }
<option value={user.username}>{user.username}</option>
{#each allUsers as invitedUser }
<option value={invitedUser.username}>{invitedUser.username}</option>
{/each}
</select>
{/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;

View File

@@ -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 [];
});
};
</script>
@@ -131,13 +134,19 @@
<fieldset>
<legend>options</legend>
<button class="pong_button" on:click={fetchMatchList}>Reload</button>
<div>
<p>sound :</p>
<input type="radio" id="sound_on" name="sound_selector" bind:group={sound} value="on">
<label for="sound_on">on</label>
<input type="radio" id="sound_off" name="sound_selector" bind:group={sound} value="off">
<label for="sound_off">off</label>
sound :
<label for="sound_on">
<input type="radio" id="sound_on" name="sound_selector" bind:group={sound} value="on">
on
</label>
<label for="sound_off">
<input type="radio" id="sound_off" name="sound_selector" bind:group={sound} value="off">
off
</label>
</div>
</fieldset>
{#if matchList.length !== 0}
<menu id="match_list">

View File

@@ -28,7 +28,7 @@
</script>
<Header />
<br />
<div class="background-pages">
<div class="principal-div">
<table class="stats-table">
<thead>
@@ -59,6 +59,7 @@
</tbody>
</table>
</div>
</div>
<style>
@@ -71,7 +72,6 @@
margin-left: auto;
margin-right: auto;
font-size: 0.9em;
font-family: sans-serif;
min-width: 400px;
}

View File

@@ -59,14 +59,20 @@ function start_after_countdown()
abortControllerKeydown = new AbortController();
window.addEventListener(
'keydown',
(e) => { pong.addKey(e.key); },
(e) => {
e.preventDefault();
pong.addKey(e.key);
},
{signal: abortControllerKeydown.signal}
);
abortControllerKeyup = new AbortController();
window.addEventListener(
'keyup',
(e) => { pong.deleteKey(e.key); },
(e) => {
e.preventDefault();
pong.deleteKey(e.key);
},
{signal: abortControllerKeyup.signal}
);

View File

@@ -19,6 +19,7 @@
<Chat color="bisque"/>
<div class="background-pages">
<div class="outer">
{#if user !== undefined}
<GenerateUserDisplay user={user}/>
@@ -28,13 +29,13 @@
<div>Failed to load current</div>
{/if}
</div>
</div>
<style>
div.outer{
max-width: 960px;
margin: 40px auto;
max-width: 100%;
text-align: center;
padding-bottom: 10px;
}
</style>

View File

@@ -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 @@
</script>
<div class="background-pages">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="top-grid">
@@ -353,7 +342,7 @@
</div>
</div>
</div>
<style>
/* ok so i want a 12 column grid with a sidebar */
@@ -368,7 +357,14 @@
div.sidebar-list{
grid-column: 1 / span 2;
background: white;
background: #FB8B24;
padding: 1vw;
max-width: 100%;
max-height: 100%;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
border-right: 4px solid #071013;
}
div.sidebar-item{
@@ -393,7 +389,7 @@
}
div.placeholder{
color: darkgray;
color: white;
text-align: center;
}
@@ -402,7 +398,7 @@
}
div.tip{
color: darkgray;
color: white;
font-size: 0.8em;
font-weight: bold;
}

View File

@@ -106,6 +106,7 @@
<main>
<div class="background-pages">
<h2>Look! You can change stuff</h2>
<div class="cards">
<Card>
@@ -114,7 +115,7 @@
<div class="label">New Username</div>
<!-- it really hates {user.username} and ${user.username} -->
<!-- <input type="text" placeholder="current username: ${user.username}" bind:value={set.username}> -->
<input type="text" placeholder="current username: {nameTmp}" bind:value={set.username}>
<input type="text" placeholder="{nameTmp}" bind:value={set.username}>
<div class="success">{success.username}</div>
<div class="error">{errors.username}</div>
</div>
@@ -144,7 +145,7 @@
</form>
</Card>
</div>
</div>
</main>
@@ -158,6 +159,8 @@
grid-gap: 20px; */
}
div.cards{
display: grid;
grid-template-columns: 1fr 1fr;
@@ -190,6 +193,7 @@
.inline-check{
display: inline;
color: #333;
}

View File

@@ -21,126 +21,84 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<header>
<div>
<img src="/img/potato_logo.png" alt="Potato Pong Logo" on:click={() => (push('/'))}>
<h1>Potato Pong</h1>
</div>
<nav>
<button class:selected="{current === '/game'}" on:click={() => (push('/game'))}>Play</button>
<button class:selected="{current === '/spectator'}" on:click={() => (push('/spectator'))}>Spectate</button>
<button class:selected="{current === '/ranking'}" on:click={() => (push('/ranking'))}>Ranking</button>
<div class="header">
<img src="/img/logo_potato.png" alt="Potato Pong Logo" on:click={() => (push('/'))}>
<div class="center">
<button class:selected="{current === '/game'}" on:click={() => (push('/game'))}>Play</button>
<button class:selected="{current === '/spectator'}" on:click={() => (push('/spectator'))}>Spectate</button>
<button class:selected="{current === '/ranking'}" on:click={() => (push('/ranking'))}>Ranking</button>
<button class:selected="{current === '/profile'}" on:click={() => (push('/profile'))}>My Profile</button>
<button class:selected="{current === '/profile/friends'}" on:click={() => (push('/profile/friends'))}>Friends</button>
<button on:click={handleClickLogout}>Log Out</button>
</nav>
</header>
</div>
<button class="logout" on:click={handleClickLogout}>Log Out</button>
</div>
<style>
/* See "possible_fonts.css" for more font options... */
@font-face {
font-family: 'Bondi';
src:url('/fonts/Bondi.ttf.woff') format('woff'),
url('/fonts/Bondi.ttf.svg#Bondi') format('svg'),
url('/fonts/Bondi.ttf.eot'),
url('/fonts/Bondi.ttf.eot?#iefix') format('embedded-opentype');
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;
}
.selected {
background-color: chocolate;
.header div.center button.selected {
text-decoration: underline;
/* TMP so it's obvious but we need to pick good colors */
background-color: #2B3A67;
}
.header div.center button {
background-color: #8c0000;
border-color: #071013;
color: white;
font-family: "Bit5x3";
border-width: 2px;
font-size: 2vw;
}
/* There is a bunch of unncessary shit in here... why so many flex grids, why is everything the same class? just seemed easier but... */
header{
/* background: #f7f7f7; */
background: #618174;
/* padding: 20px; */
margin: 0;
/* does nothing so far... */
/* display: flex; */
.header button.logout {
background-color: #008c8c;
border-color: #071013;
color: white;
font-family: "Bit5x3";
border-width: 2px;
font-size: 2vw;
}
header{
/* for some reason this doesn't do shit! */
position: sticky;
/* position: absolute; */
display: flex;
justify-content: space-between;
flex-wrap: wrap;
/* display: grid; */
/* grid-template-columns: 1fr 1fr 1fr; */
.header {
resize: vertical;
overflow: hidden;
background: #FB8B24;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
border-bottom: 4px solid #071013;
position: relative;
display: block;
padding: 10px;
}
/* Headers */
h1{
font-family: 'Bondi';
}
h1{
margin: 0;
text-align: left;
/* max-width: 40px; */
/* this helped with the weird extra space under the image... */
display: flex;
.header div.center {
width: fit-content;
justify-self: center;
align-self: center;
position: absolute;
left: 50%;
display: inline-block;
-ms-transform: translateX(-50%);
transform: translateX(-50%);
}
/* Images */
.header button.logout {
float: right;
}
img{
.header img{
cursor: pointer;
max-width: 40px;
padding: 7px 20px;
justify-self: left;
max-width: 10%;
float: left;
}
header div {
display: flex;
justify-self: center;
align-self: center;
}
nav{
display: flex;
/* justify-content: flex-end; */
justify-content: space-evenly;
flex-wrap: wrap;
}
nav button{
margin: 7px 20px;
/* margin: 7px auto; */
/* padding: 5px; */
border-radius: 4px;
}
@media screen and (max-width: 350px) {
nav button{
width: 100%;
}
header div{
flex-wrap: wrap;
display: flex;
justify-content: center;
}
}
</style>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import Layouts from './Chat_layouts.svelte';
import { init_socket } from './Socket_init';
import { init_socket } from './Socket_chat';
export let color = "transparent";

View File

@@ -1,7 +1,5 @@
<script lang="ts">
import Debug from './tmp_debug.svelte';
import { layout } from './Store_chat';
import ChatBox from './Chat_box_css.svelte';
@@ -11,10 +9,11 @@
import NewLayout from './Layout_new.svelte';
import SettingsLayout from './Layout_settings.svelte';
import RoomsetLayout from './Layout_room_set.svelte';
import ProtectedLayout from './Layout_protected.svelte';
import PasswordLayout from './Layout_password.svelte';
import CreateLayout from './Layout_create.svelte';
import MuteLayout from './Layout_mute.svelte';
import UserLayout from './Layout_user.svelte';
import InviteLayout from './Layout_invite.svelte';
import Button from './Element_button.svelte';
@@ -29,8 +28,8 @@
*/
function set_layouts($layout)
{
console.log("layouts:", layouts);
console.log("layout:", $layout);
//console.log("layouts:", layouts);
//console.log("layout:", $layout);
if ($layout.length === 0)
layout.set(layouts[0]);
else if ($layout === "close")
@@ -41,7 +40,7 @@
layouts = [$layout, "home"];
else
layouts = [$layout, layouts[0]];
console.log("- layouts:", layouts);
//console.log("- layouts:", layouts);
}
$: set_layouts($layout);
@@ -67,8 +66,17 @@
{:else if $layout === "room_set"}
<RoomsetLayout back={layouts[1]} />
{:else if $layout === "protected"}
<ProtectedLayout back={layouts[1]} />
{:else if $layout === "password"}
<PasswordLayout back={layouts[1]} />
{:else if $layout === "add_password"}
<PasswordLayout back={layouts[1]} mode="add" />
{:else if $layout === "change_password"}
<PasswordLayout back={layouts[1]} mode="change" />
{:else if $layout === "remove_password"}
<PasswordLayout back={layouts[1]} mode="remove" />
{:else if $layout === "create"}
<CreateLayout back={layouts[1]} />
@@ -79,12 +87,12 @@
{:else if $layout === "user"}
<UserLayout back={layouts[1]} />
{:else if $layout === "invite"}
<InviteLayout back={layouts[1]} />
{/if}
</ChatBox>
<!-- TMP DEBUG -->
<!-- <Debug bind:layouts /> -->
<style></style>

View File

@@ -2,7 +2,6 @@
<Button
bind:layout
new_layout=""
on_click={}
my_class=""
my_title=""
>
@@ -17,7 +16,6 @@
export let my_class = "";
export let my_title = "";
export let new_layout = "";
export let on_click = "";
function update_layout() {
layout.set(new_layout);
@@ -25,7 +23,7 @@
</script>
<button on:click={update_layout} on:click={on_click} title={my_title} class={my_class}>
<button on:click={update_layout} on:click title={my_title} class={my_class}>
<p><slot></slot></p>
</button>

View File

@@ -1,6 +0,0 @@
export interface Room
{
name: string;
type: "public" | "protected" | "private" | "direct" | "user";
users?: string[];
}

View File

@@ -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 @@
<!-- panel_create -->
<div class="panel panel_create __border_top">
<form on:submit|preventDefault={handleSubmit}>
{#if response.status >= 300}
{#if show_error}
<Warning content={response.message}/>
{/if}
<!-- name: -->
@@ -82,21 +87,19 @@
<input id="chat_public" bind:group={room_type} type="radio" name="room_type" value="public" required>
</label>
<!-- [ ] private -->
<label for="chat_private" class="_radio hide">
<label for="chat_private" class="_radio">
<p>private</p>
<input id="chat_private" bind:group={room_type} type="radio" name="room_type" value="private" required>
</label>
<!-- [ ] protected -->
<label for="chat_protected" class="_radio hide">
<label for="chat_protected" class="_checkbox">
<p>protected</p>
<input id="chat_protected" bind:group={room_type} type="radio" name="room_type" value="protected" required>
<input id="chat_protected" bind:checked={is_protected} type="checkbox" name="room_type" value="protected">
</label>
<!-- [x] protected -->
{#if room_type === 'protected'}
<div>
<label for="chat_pswd"><p>choose a password :</p></label>
<input id="chat_pswd" bind:value={room_password} type="password" placeholder="minimum 8 characters" minlength="8" name="password" required>
</div>
{#if is_protected === true}
<label for="chat_pswd">choose a password :</label>
<input id="chat_pswd" bind:value={room_password} type="password" placeholder="minimum 8 characters" minlength="8" name="password" required>
{/if}
<input type="submit" value="&#x2BA1">
</form>
@@ -119,24 +122,25 @@
/ auto 1fr auto ;
}
/* temp
*/
.hide {
display: none !important;
}
/* radio elements style check
*/
.panel label._radio {
.panel label {
display: inline;
margin: 10px 0px 0px auto;
padding-right: 10px;
cursor: pointer;
padding-right: 10px;
margin: 10px 0px 10px auto;
}
.panel label._radio * {
.panel label._radio {
margin-bottom: 0px;
}
.panel label._checkbox {
margin-top: 20px;
}
.panel label * {
display: inline;
}
.panel label._radio p {
.panel label p {
margin-top: 0px;
margin-bottom: 0px;
}

View File

@@ -1,6 +1,6 @@
<script>
import { layout, msgs, user } from './Store_chat';
import { layout, msgs, user, current_room } from './Store_chat';
import { change_room, get_room_messages, get_my_rooms } from './Request_rooms';
import { onMount } from 'svelte';
import Button from './Element_button.svelte';
@@ -13,8 +13,16 @@
console.log("inside go_to_room");
console.log("room:", room);
await change_room(room);
await get_room_messages();
if (room.protection && !room.allowed)
{
await current_room.set(room);
layout.set("password");
}
else
{
await change_room(room);
await get_room_messages();
}
}
</script>
@@ -47,8 +55,8 @@
<p>rooms are loading...</p>
{:then rooms}
{#each rooms as room}
<Button my_class="list" on_click={function() {go_to_room(room)}}>
{room.name}
<Button my_class="list" on:click={function() {go_to_room(room)}}>
{room.client_name}
</Button>
{/each}
{/await}

View File

@@ -0,0 +1,84 @@
<script lang="ts">
import { layout, user, current_room } from './Store_chat';
import { get_all_users, invite_user } from './Request_rooms';
import Button from './Element_button.svelte';
export let back = "";
let users = get_all_users();
// invite user in this room
async function invite_this_user(username: string)
{
console.log("inside invite_this_user");
invite_user(username);
layout.set("room");
}
</script>
<div class="grid_box">
<!-- back -->
<Button new_layout={back} my_class="back icon" my_title="go back {back}">
back
</Button>
<!-- new -->
<Button my_class="invite deactivate">
invite
</Button>
<!-- close -->
<Button new_layout="close" my_class="close icon">
close
</Button>
<!-- room_name -->
<Button my_class="room_name deactivate __border_top">
{$current_room.name}
</Button>
<!-- panel_new -->
<div class="panel panel_invite __border_top">
<p>invite someone in this room :</p>
<div>
<div class="__show_if_only_child">
<p class="__center">/ there is no one to invite yet /</p>
</div>
{#await users}
<p>users are loading...</p>
{:then users}
{#each users as user}
<Button my_class="list" on:click={function() {invite_this_user(user.username)}}>
{user.username}
</Button>
{/each}
{/await}
</div>
</div>
</div>
<style>
/* grid layout "new"
*/
.grid_box :global(.back ) {grid-area: back;}
.grid_box :global(.invite ) {grid-area: invite;}
.grid_box :global(.close ) {grid-area: close;}
.grid_box :global(.room_name ) {grid-area: room_name;}
.grid_box :global(.panel_invite) {grid-area: panel_invite;}
.grid_box {
grid:
' back invite close ' auto
' room_name room_name room_name ' auto
' panel_invite panel_invite panel_invite ' 1fr
/ auto 1fr auto ;
}
</style>

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { layout, msgs, user, socket } from './Store_chat';
import { layout, msgs, user, socket, current_room } from './Store_chat';
import { join_room, change_room, get_room_messages, get_all_rooms } from './Request_rooms';
import Button from './Element_button.svelte';
@@ -13,10 +13,14 @@
{
console.log("inside join_room");
console.log("room:", room);
const updated_room = await join_room(room);
console.log("updated room:", updated_room);
await change_room(updated_room);
if (updated_room.protection)
{
current_room.set(updated_room);
layout.set("password");
}
else
await change_room(updated_room);
}
</script>
@@ -52,7 +56,7 @@
<p>rooms are loading...</p>
{:then rooms}
{#each rooms as room}
<Button my_class="list" on_click={function() {join_rooms(room)}}>
<Button my_class="list" on:click={function() {join_rooms(room)}}>
{room.name}
</Button>
{/each}

View File

@@ -0,0 +1,121 @@
<script lang="ts">
import { layout, current_room } from './Store_chat';
import { change_room, validate_password, change_password, add_password, remove_password } from './Request_rooms';
import { FetchResponse } from './Types_chat';
import Button from './Element_button.svelte';
import Warning from './Element_warning.svelte';
export let back = "";
export let mode = "validate";
let password_state = "";
if (mode === 'change')
password_state = "new";
if (mode === 'remove')
password_state = "current";
let room_password: string;
let room_old_password: string;
let response: FetchResponse;
let show_error = false;
async function handleSubmit(evt)
{
console.log("in handleSubmit");
let formIsValid = evt.target.checkValidity();
if (!formIsValid)
return;
let room = {
name: $current_room.name,
type: $current_room.type,
password: room_password,
};
room.protection = true;
if (mode === 'remove')
room.protection = false;
// send password
if (mode === 'validate')
response = await validate_password(room);
if (mode === 'add')
response = await add_password(room);
if (mode === 'change')
response = await change_password(room, room_old_password);
if (mode === 'remove')
response = await remove_password(room);
// go to room
if (response.status >= 300 || response.error)
show_error = response.error;
else
await change_room(response.room);
}
</script>
<div class="grid_box">
<!-- back -->
<Button new_layout={back} my_class="back icon" my_title="go back {back}">
back
</Button>
<!-- room_name -->
<Button my_class="room_name deactivate">
{$current_room.name}
</Button>
<!-- close -->
<Button new_layout="close" my_class="close icon">
close
</Button>
<!-- panel_password -->
<div class="panel panel_password __border_top">
<p class="title __center">this room is protected</p>
<form on:submit|preventDefault={handleSubmit}>
{#if show_error}
<Warning content={response.message}/>
{/if}
{#if mode === 'change'}
<label for="chat_old_pswd"><p>enter old password :</p></label>
<input id="chat_old_pswd" bind:value={room_old_password} type="password" placeholder="minimum 8 characters" minlength="8" name="old_password" required>
{/if}
<label for="chat_pswd"><p>enter {password_state} password :</p></label>
<input id="chat_pswd" bind:value={room_password} type="password" placeholder="minimum 8 characters" minlength="8" name="password" required>
<input type="submit" value="&#x2BA1">
</form>
</div>
</div>
<style>
/* grid layout "password"
*/
.grid_box :global(.back ) {grid-area: back;}
.grid_box :global(.room_name ) {grid-area: room_name;}
.grid_box :global(.close ) {grid-area: close;}
.grid_box :global(.panel_password) {grid-area: panel_password;}
.grid_box {
grid:
' back room_name close ' auto
' panel_password panel_password panel_password ' 1fr
/ auto 1fr auto ;
}
/* submit
*/
form input[type=submit] {
margin-top: 20px;
}
</style>

View File

@@ -1,65 +0,0 @@
<script>
import { layout } from './Store_chat';
import Button from './Element_button.svelte';
export let back = "";
</script>
<div class="grid_box">
<!-- back -->
<Button new_layout={back} my_class="back icon" my_title="go back {back}">
back
</Button>
<!-- room_name -->
<Button my_class="room_name deactivate">
&lt;room_name&gt;
</Button>
<!-- close -->
<Button new_layout="close" my_class="close icon">
close
</Button>
<!-- panel_protected -->
<div class="panel panel_protected __border_top">
<p class="title __center">this room is protected</p>
<form>
<label for="chat_pswd"><p>password :</p></label>
<input id="chat_pswd" type="password" required>
<input type="submit" value="&#x2BA1">
</form>
</div>
</div>
<style>
/* grid layout "protected"
*/
.grid_box :global(.back ) {grid-area: back;}
.grid_box :global(.room_name ) {grid-area: room_name;}
.grid_box :global(.close ) {grid-area: close;}
.grid_box :global(.panel_protected) {grid-area: panel_protected;}
.grid_box {
grid:
' back room_name close ' auto
' panel_protected panel_protected panel_protected ' 1fr
/ auto 1fr auto ;
}
/* submit
*/
form input[type=submit] {
margin-top: 20px;
}
</style>

View File

@@ -1,6 +1,6 @@
<script>
import { layout, socket, msgs, add_msg, current_room_name } from './Store_chat';
import { layout, socket, msgs, add_msg, current_room } from './Store_chat';
import Button from './Element_button.svelte';
import Msg from './Element_msg.svelte';
@@ -42,7 +42,7 @@
<!-- room_name -->
<Button new_layout="room_set" my_class="room_name transparent">
{$current_room_name}
{$current_room.name}
</Button>
<!-- close -->
@@ -71,7 +71,7 @@
</div>
<!-- send -->
<Button my_class="send" on_click={send_msg}>
<Button my_class="send" on:click={send_msg}>
send
</Button>

View File

@@ -1,22 +1,24 @@
<script>
import { layout, current_room_name } from './Store_chat';
import { get_room_users, user_leave_room } from './Request_rooms';
import { layout, current_room } from './Store_chat';
import { get_room_users, leave_room } from './Request_rooms';
import Button from './Element_button.svelte';
export let back = "";
let users = get_room_users();
console.log("current_room:", $current_room);
function user_profile()
{
console/log("in user_profile");
console.log("in user_profile");
}
function leave_room()
function user_leave_room()
{
console.log("in leave_room");
user_leave_room();
leave_room();
layout.set("home");
}
@@ -31,7 +33,7 @@
<!-- room_name -->
<Button my_class="room_name deactivate">
{$current_room_name}
{$current_room.name}
</Button>
<!-- close -->
@@ -41,9 +43,27 @@
<!-- panel_room_set -->
<div class="panel panel_room_set __border_top">
<Button on_click={leave_room}>
leave
</Button>
{#if $current_room.type !== "direct"}
<Button on:click={user_leave_room}>
leave
</Button>
<Button new_layout="invite">
invite someone
</Button>
{/if}
{#if $current_room.protection }
<p class="__center">this room is password protected</p>
<Button new_layout="change_password">
change password
</Button>
<Button new_layout="remove_password">
remove password
</Button>
{:else}
<Button new_layout="add_password">
add password
</Button>
{/if}
<p>room users :</p>
<div class="room_users">
<div class="__show_if_only_child">
@@ -53,7 +73,7 @@
<p>list of users is loading...</p>
{:then users}
{#each users as user}
<Button new_layout="user" my_class="list" on_click={user_profile}>
<Button new_layout="user" my_class="list" on:click={user_profile}>
{user}
</Button>
{/each}

View File

@@ -8,6 +8,14 @@
let mute = "mute";
let block = "block";
import { push } from "svelte-spa-router";
import { invited_username } from '../store_invitation';
function game_invitation()
{
const usernamePLACEHOLDER = "hulamy";
invited_username.set(usernamePLACEHOLDER);
push("/game");
}
</script>
@@ -41,7 +49,7 @@
<Button>
view profile
</Button>
<Button>
<Button on_click={() => game_invitation()}>
game invitation
</Button>
<Button>

View File

@@ -1,12 +1,14 @@
import { msgs, user, layout, socket, current_room_name } from './Store_chat';
import type { Room } from './Interface_chat';
import { msgs, user, layout, socket, current_room } from './Store_chat';
import { Room, FetchResponse, FetchMethod } from './Types_chat';
import { fetch_chat_request, set_client_name_on_room, fill_fetch_response } from './Request_utils';
export async function get_room_messages()
{
console.log("in get_room_messages");
const response = await fetch('/api/v2/chat/messages');
const data = await response.json();
const messages = data.messages;
let response: FetchResponse = await fetch_chat_request('messages', FetchMethod.GET);
const messages = response.messages;
if (messages === null)
return;
@@ -24,102 +26,141 @@ export async function create_room(room: Room)
{
console.log("in create_room");
// send the new room
const response = await fetch('/api/v2/chat/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(room),
});
console.log("room sent to create:", room);
let response: FetchResponse = await fetch_chat_request('create', FetchMethod.POST, room);
console.log("room returned from create:", response.room);
// get response status and message
let response_status = response.status;
let data = await response.json();
let response_message = "";
return {
status: response_status,
message: data.message,
room: data.room,
};
return response;
}
export async function join_room(room: Room)
{
console.log("in join_room");
const response = await fetch('/api/v2/chat/join', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(room),
});
let data = await response.json();
console.log("room sent to join:", room);
let response: FetchResponse = await fetch_chat_request('join', FetchMethod.POST, room);
console.log("room returned from join:", response.room);
return data.room;
return response.room;
}
export async function change_room(room: Room)
{
console.log("in change_room");
console.log("room:", room);
const response = await fetch('/api/v2/chat/change', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(room),
});
let data = await response.json();
console.log("room sent to change:", room);
let response: FetchResponse = await fetch_chat_request('change', FetchMethod.POST, room);
console.log("room returned from change:", response.room);
await get_room_messages();
let room_name = data.room.name;
if (room.type === 'direct')
{
room_name === room.users[0];
if (room_name === user.username)
room_name === room.users[1];
}
current_room_name.set(room_name);
set_client_name_on_room(room);
current_room.set(room);
layout.set("room");
}
export async function validate_password(room: Room)
{
console.log("in validate_password");
console.log("room sent to validate password:", room);
let response: FetchResponse = await fetch_chat_request('passwordauth', FetchMethod.POST, room);
console.log("room returned from validate password:", response.room);
return response;
}
export async function add_password(room: Room)
{
console.log("in add_password");
console.log("room sent to add password:", room);
let response: FetchResponse = await fetch_chat_request('addpassword', FetchMethod.POST, room);
console.log("room returned from add password:", response.room);
return response;
}
export async function change_password(room: Room, old_password: string)
{
console.log("in send_password");
let request_body =
{
room: room,
old_password: old_password,
}
console.log("room sent to change password:", room);
let response: FetchResponse = await fetch_chat_request('changepassword', FetchMethod.POST, request_body);
console.log("room returned from change password:", response.room);
return response;
}
export async function remove_password(room: Room)
{
console.log("in send_password");
console.log("room sent to remove password:", room);
let response: FetchResponse = await fetch_chat_request('removepassword', FetchMethod.DELETE, room);
console.log("room returned from remove password:", response.room);
return response;
}
export async function invite_user(user_name: string)
{
console.log("in invite_user");
let response: FetchResponse = await fetch_chat_request('invite', FetchMethod.POST, {username: user_name});
await get_room_messages();
}
export async function get_my_rooms()
{
console.log("in get_my_rooms");
const response = await fetch('/api/v2/chat/myrooms');
const data = await response.json();
console.log("data.rooms:", data.rooms);
let response: FetchResponse = await fetch_chat_request('myrooms', FetchMethod.GET);
return data.rooms;
let rooms = response.rooms.map(room => set_client_name_on_room(room));
return rooms;
}
export async function get_all_rooms()
{
console.log("in get_all_rooms");
const response = await fetch('/api/v2/chat/allrooms');
const data = await response.json();
let response: FetchResponse = await fetch_chat_request('allrooms', FetchMethod.GET);
return data.rooms;
return response.rooms;
}
export async function get_room_users()
{
console.log("in get_room_users");
const response = await fetch('/api/v2/chat/roomusers');
const data = await response.json();
let response: FetchResponse = await fetch_chat_request('roomusers', FetchMethod.GET);
return data.users;
return response.users;
}
export async function user_leave_room()
export async function get_all_users()
{
console.log("in get_all_users");
let response: FetchResponse = await fetch_chat_request('users', FetchMethod.GET);
return response.users;
}
export async function leave_room()
{
console.log("in leave_room");
const response = await fetch('/api/v2/chat/removeuser', {
method: 'DELETE',
});
let response: FetchResponse = await fetch_chat_request('leave', FetchMethod.DELETE);
}

View File

@@ -0,0 +1,59 @@
import { user } from './Store_chat';
import { Room, FetchResponse, FetchInit, FetchMethod } from './Types_chat';
export async function fetch_chat_request(route: string, fetchMethod: FetchMethod, param?: any)
{
console.log("in fetch_chat_request");
let response: FetchResponse = { status: 0 };
let fetch_params: FetchInit = {
method: fetchMethod,
headers: { 'Content-Type': 'application/json' },
}
if (param)
fetch_params.body = JSON.stringify(param);
try
{
const resp = await fetch(`/api/v2/chat/${route}`, fetch_params);
response.status = resp.status;
let data = await resp.json();
fill_fetch_response(response, data);
if (!resp.ok)
throw new Error(data.message);
}
catch (error)
{
console.error(error.message);
}
return response;
}
export function set_client_name_on_room(room: Room)
{
console.log("in set_client_name_on_room");
if (room.type === 'direct')
{
room.client_name = room.users[0];
if (room.client_name === user.username)
room.client_name = room.users[1];
}
else
room.client_name = room.name;
return room;
}
export function fill_fetch_response(response: FetchResponse, data: any)
{
console.log("in fill_fetch_response");
Object.keys(data).forEach(key =>
{
response[key] = data[key];
});
}

View File

@@ -1,6 +1,6 @@
import io from 'socket.io-client';
import { set_socket, set_user } from './Store_chat';
import { socket_events } from './Socket_events';
import { user, msgs } from './Store_chat';
const address = `http://${process.env.WEBSITE_HOST}:${process.env.WEBSITE_PORT}`;
@@ -21,6 +21,23 @@ export async function init_socket()
});
set_socket(socket);
socket_states(socket);
socket_events(socket);
}
function socket_events(socket)
{
socket.on('message', function(from, message)
{
console.log("received msg:", message, from);
if (from === user.username)
from = "me";
msgs.update(msgs => [...msgs, { name: from, message: message }]);
});
}
function socket_states(socket)
{
socket.on('connect', function(){ console.log("socket.io connected"); });
socket.on('disconnect', function(){ console.log("socket.io disconnected"); });
socket.on('connect_error', function(){ console.log("socket.io connect_error"); });
@@ -33,7 +50,5 @@ export async function init_socket()
socket.on('reconnect_failed', function(){ console.log("socket.io reconnect_failed"); });
socket.on('ping', function(){ console.log("socket.io ping"); });
socket.on('pong', function(){ console.log("socket.io pong"); });
socket_events(socket);
}

View File

@@ -1,12 +0,0 @@
import { user, msgs } from './Store_chat';
export function socket_events(socket)
{
socket.on('message', function(from, message)
{
console.log("received msg:", message, from);
if (from === user.username)
from = "me";
msgs.update(msgs => [...msgs, { name: from, message: message }]);
});
}

View File

@@ -1,8 +1,13 @@
import { writable } from 'svelte/store';
import { Room } from './Types_chat';
export let msgs = writable([]);
export let layout = writable("close");
export let current_room_name = writable("");
export let current_room: Room = writable({
name: "",
type: "",
protection: false,
});
export let user;
export let socket;

View File

@@ -0,0 +1,33 @@
export interface Room
{
name: string;
type: "public" | "private" | "direct" | "user";
users?: string[];
client_name?: string;
protection: boolean;
allowed?: boolean;
}
export interface FetchResponse
{
status: number;
error?: boolean;
code?: string;
message?: string;
room?: Room;
}
export interface FetchInit
{
method: string;
headers: any;
body?: string;
}
export enum FetchMethod
{
POST = 'POST',
GET = 'GET',
DELETE = 'DELETE',
}

View File

@@ -1,27 +0,0 @@
<script>
export let layout = "";
export let layouts = [];
</script>
<div style="display: flex; flex-direction: column; font-size: 12px; position: fixed; top: 20px; left: 20px; background-color: white;">
<p>temp, for testing :</p>
<button on:click={function(){layout = "close" }}>close</button>
<button on:click={function(){layout = "home" }}>home</button>
<button on:click={function(){layout = "room" }}>room</button>
<button on:click={function(){layout = "new" }}>new</button>
<button on:click={function(){layout = "settings" }}>settings</button>
<button on:click={function(){layout = "room_set" }}>room_set</button>
<button on:click={function(){layout = "protected"}}>protected</button>
<button on:click={function(){layout = "create" }}>create</button>
<button on:click={function(){layout = "mute" }}>mute</button>
<button on:click={function(){
layouts = ["settings", "settings"];
layout = "user";
}}>user from settings</button>
<button on:click={function(){
layouts = ["room_set", "room_set"];
layout = "user";
}}>user from room_set</button>
</div>

View File

@@ -0,0 +1,4 @@
import { writable } from 'svelte/store';
export const invited_username = writable("");

View File

@@ -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 [];
});
}