ilhamdev commited on
Commit
ece5841
·
verified ·
1 Parent(s): 300d73e

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. .github/workflows/main.yml +19 -0
  3. README.md +84 -11
  4. backend/.dockerignore +1 -0
  5. backend/.env.example +22 -0
  6. backend/.gitignore +4 -0
  7. backend/app.js +74 -0
  8. backend/chessbot.js +28 -0
  9. backend/constants.js +16 -0
  10. backend/dockerfile +23 -0
  11. backend/engine/stockfish16 +3 -0
  12. backend/mail.js +30 -0
  13. backend/models/challenge.js +30 -0
  14. backend/models/game.js +32 -0
  15. backend/models/user.js +87 -0
  16. backend/package-lock.json +1547 -0
  17. backend/package.json +29 -0
  18. backend/routes/auth.js +120 -0
  19. backend/routes/public.js +102 -0
  20. backend/routes/room.js +54 -0
  21. backend/routes/user.js +294 -0
  22. backend/socket.js +200 -0
  23. backend/util/auth.js +72 -0
  24. backend/util/errors.js +23 -0
  25. backend/util/user.js +0 -0
  26. backend/util/validation.js +21 -0
  27. docker-compose-prod.yml +0 -0
  28. docker-compose.yml +20 -0
  29. frontend/.dockerignore +2 -0
  30. frontend/.env.example +1 -0
  31. frontend/.eslintignore +5 -0
  32. frontend/.eslintrc.cjs +37 -0
  33. frontend/.gitignore +27 -0
  34. frontend/dockerfile +20 -0
  35. frontend/index.html +23 -0
  36. frontend/package-lock.json +0 -0
  37. frontend/package.json +54 -0
  38. frontend/public/assets/audio/capture.mp3 +0 -0
  39. frontend/public/assets/audio/castle.mp3 +0 -0
  40. frontend/public/assets/audio/game-end.webm +0 -0
  41. frontend/public/assets/audio/move-check.mp3 +0 -0
  42. frontend/public/assets/audio/move-self.mp3 +0 -0
  43. frontend/public/assets/audio/notify.mp3 +0 -0
  44. frontend/public/assets/audio/promote.mp3 +0 -0
  45. frontend/public/assets/images/bishop_black.png +0 -0
  46. frontend/public/assets/images/bishop_white.png +0 -0
  47. frontend/public/assets/images/chess_board.png +0 -0
  48. frontend/public/assets/images/chess_board_loader.png +0 -0
  49. frontend/public/assets/images/king_black.png +0 -0
  50. frontend/public/assets/images/king_white.png +0 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ backend/engine/stockfish16 filter=lfs diff=lfs merge=lfs -text
.github/workflows/main.yml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Sync to Hugging Face hub
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ # to run this workflow manually from the Actions tab
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ sync-to-hub:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v3
13
+ with:
14
+ fetch-depth: 0
15
+ lfs: true
16
+ - name: Push to hub
17
+ env:
18
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
19
+ run: git push https://ilhamdev:$HF_TOKEN@huggingface.co/spaces/ilhamdev/chessGame main
README.md CHANGED
@@ -1,11 +1,84 @@
1
- ---
2
- title: ChessGame
3
- emoji: 🦀
4
- colorFrom: yellow
5
- colorTo: yellow
6
- sdk: docker
7
- pinned: false
8
- license: other
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ChessHub
2
+ ChessHub is a web application for chess enthusiasts that allows players to compete against each other.
3
+
4
+ ![Capture](https://github.com/moonpatel/ChessHub/assets/95487062/d4bc47fe-9364-43e6-8a6e-081daee55d98)
5
+
6
+
7
+ ## Technologies used
8
+ <p>
9
+ <a href="https://react.dev/"><img src="https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB" /></a>
10
+ <a href="https://www.mongodb.com/"><img src="https://img.shields.io/badge/MongoDB-4EA94B?style=for-the-badge&logo=mongodb&logoColor=white" /></a>
11
+ <a href="https://nodejs.org/en"><img src="https://img.shields.io/badge/Node%20js-339933?style=for-the-badge&logo=nodedotjs&logoColor=white" /></a>
12
+ <a href="https://expressjs.com/"><img src="https://img.shields.io/badge/Express%20js-000000?style=for-the-badge&logo=express&logoColor=white" /></a>
13
+ <a href="https://socket.io/"><img src="https://img.shields.io/badge/Socket.io-010101?&style=for-the-badge&logo=Socket.io&logoColor=white" /></a>
14
+ </p>
15
+
16
+ ## Features
17
+ 1. Play chess with your friends
18
+ 2. Play with computer with customizable ELO
19
+ 3. View your game history
20
+ 4. Make friends
21
+ 5. Analyze your games
22
+
23
+ ## How to contribtute to this repository ?
24
+ You can contribute to this repository by checking out existing issues or creating your own in the issue section (if you experience any bugs in the application or you want to propose a new feature).
25
+
26
+ ### Setting up the project locally
27
+ 1. Make sure to star the repository before going ahead.
28
+ 2. Fork the repo. (Click on the fork button in the top right corner).
29
+ 3. Clone the forked repo to your local machine.
30
+ ```
31
+ git clone https://github.com/moonpatel/ChessHub.git
32
+ ```
33
+ 4. Change the present working directory.
34
+ ```
35
+ cd ChessHub
36
+ ```
37
+ 5. Create a new branch.
38
+ ```
39
+ git checkout -b new-branch
40
+ ```
41
+ 6. Create a .env file in both frontend and backend directory according to the .env.example files with the required environment variables. The stockfish chess engine binary in the repo is for Linux systems only. If you are not using Linux then download the required stockfish chess engine binary from <a href="https://stockfishchess.org/download/">stockfish</a> website and place it in backend/engine directory. Also add the path of the engine to the CHESS_ENGINE_PATH variable in .env file in backend directory.
42
+ 7. Install the dependencies for frontend.
43
+ ```
44
+ cd frontend
45
+ npm install
46
+ ```
47
+ 8. Install the dependencies in the backend.
48
+ ```
49
+ cd backend
50
+ npm install
51
+ ```
52
+ 9. Start the frontend
53
+ ```
54
+ cd frontend
55
+ npm run dev
56
+ ```
57
+ 10. Start the backend
58
+ ```
59
+ cd backend
60
+ npm run dev
61
+ ```
62
+ 11. Visit http://localhost:5173 in your browser to view the application.
63
+
64
+ ### Setting up project using docker (Recommended)
65
+ There is an alternative option to set up the project using docker. Make sure you have docker and docker-compose installed on your system.
66
+ 1. Fork the repo. (Click on the fork button in the top right corner).
67
+ 2. Clone the forked repo to your local machine.
68
+ ```
69
+ git clone https://github.com/moonpatel/ChessHub.git
70
+ ```
71
+ 3. Change the present working directory.
72
+ ```
73
+ cd ChessHub
74
+ ```
75
+ 4. Add .env files to both your frontend and backend directories based on their respective .env.example templates (can be found inside the directories itself).
76
+ 5. Build the docker images for frontend and backend.
77
+ ```
78
+ docker-compose build
79
+ ```
80
+ 6. Run the docker containers.
81
+ ```
82
+ docker-compose up
83
+ ```
84
+ 7. Visit the website from your browser -> http://localhost:5173
backend/.dockerignore ADDED
@@ -0,0 +1 @@
 
 
1
+ node_modules
backend/.env.example ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # use email only if you want to send emails
2
+ # email id used for sending mails
3
+ EMAIL_ID=
4
+ MAIL_SERVER_PASSWORD=
5
+
6
+ # host that are allowed in backend
7
+ CORS_ALLOWED_HOST=http://localhost:5173
8
+
9
+ # connection string to connect to MongoDB database. Can be local or MongoDB Atlas
10
+ CONNECTION_STRING=
11
+
12
+ PORT=8080
13
+
14
+ # path to the chess engine
15
+ CHESS_ENGINE_PATH=./engine/stockfish16.exe
16
+
17
+ # hostname
18
+ HOSTNAME=http://localhost:8080
19
+ # can be PROD or DEV
20
+ # DEV - development mode
21
+ # PROD - same as DEV but serves frontend statics files from ../frontend/dist using the / endpoint
22
+ MODE=DEV
backend/.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ node_modules
2
+ _test_
3
+ .env
4
+ engine/stockfish
backend/app.js ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const path = require("path");
2
+
3
+ const express = require("express");
4
+ const bodyParser = require("body-parser");
5
+ const cors = require("cors");
6
+ const authRoutes = require("./routes/auth");
7
+ const userRoutes = require("./routes/user");
8
+ const roomRoutes = require("./routes/room");
9
+ const mongoose = require("mongoose");
10
+ const cookieParser = require("cookie-parser");
11
+ require("dotenv").config();
12
+
13
+ const MODE = process.env.MODE || "DEV"
14
+ const port = process.env.PORT;
15
+
16
+ mongoose
17
+ .connect(process.env.CONNECTION_STRING)
18
+ .then((res) => console.log("Connected to MongoDB"))
19
+ .catch((err) => console.log("Error in connecting to database"));
20
+
21
+ const app = express();
22
+ const http = require("http");
23
+ const server = http.createServer(app);
24
+ const { socketIOServerInit } = require("./socket");
25
+
26
+ app.get("/health-check", (req, res, next) => {
27
+ res.status(200).send("OK");
28
+ });
29
+ app.use(cors({ origin: process.env.CORS_ALLOWED_HOST, credentials: true }));
30
+ app.use(bodyParser.json());
31
+ app.use((req, res, next) => {
32
+ // res.setHeader("Access-Control-Allow-Origin", "http://localhost:5173");
33
+ // res.setHeader("Access-Control-Allow-Methods", "GET,POST,PATCH,DELETE");
34
+ // res.setHeader("Access-Control-Allow-Headers", "Content-Type,Authorization");
35
+ next();
36
+ });
37
+ app.use(cookieParser());
38
+
39
+ socketIOServerInit(server);
40
+
41
+ app.use((req, res, next) => {
42
+ console.log(req.url);
43
+ next();
44
+ });
45
+
46
+ // app.use("/", (req, res, next) => res.send('Hello'));
47
+ app.use("/api/auth", authRoutes);
48
+ app.use("/api/user", userRoutes);
49
+ app.use("/api/room", roomRoutes);
50
+
51
+ if (MODE=="PROD") {
52
+ console.log('Hello')
53
+ app.use(express.static('../frontend/dist'));
54
+ app.get("/*", (req,res,next) => {
55
+ try {
56
+ res.sendFile(path.join(__dirname,"../frontend/dist/index.html"))
57
+ } catch(err) {
58
+ next(err)
59
+ }
60
+ })
61
+ }
62
+
63
+ app.use((error, req, res, next) => {
64
+ const status = error.status || 500;
65
+ console.log(error);
66
+ res.status(status).json({
67
+ message: "Something went wrong",
68
+ description: error?.message || "Internal server error",
69
+ });
70
+ });
71
+
72
+ server.listen(port, () => {
73
+ console.log("Listening on server", port);
74
+ });
backend/chessbot.js ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const path = require("path");
2
+ const { Engine } = require("node-uci");
3
+
4
+ require("dotenv").config();
5
+ const engine = new Engine(path.join(__dirname, process.env.CHESS_ENGINE_PATH || "engine/stockfish16.exe"));
6
+
7
+ engine
8
+ .init()
9
+ .then((eng) => {
10
+ return eng.setoption("UCI_LimitStrength", true);
11
+ })
12
+ .then((eng) => {
13
+ eng.setoption("UCI_Elo");
14
+ })
15
+ .catch((err) => {
16
+ console.error(err);
17
+ });
18
+
19
+ const nextMove = async ({ position }) => {
20
+ await engine.isready();
21
+ console.log("Chess engine ready");
22
+ engine.position(position);
23
+ const result = await engine.go({ depth: 10 });
24
+ console.log("Best move or position", position, "is", result.bestmove);
25
+ return result.bestmove;
26
+ };
27
+
28
+ module.exports = { nextMove };
backend/constants.js ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const SOCKET_EVENTS = {
2
+ CONNECTION: "connection",
3
+ JOIN_ROOM: "join-room",
4
+ JOIN_ROOM_SUCCESS: "join-room-success",
5
+ JOIN_ROOM_ERROR: "join-room-error",
6
+ ROOM_FULL: "room-full",
7
+ CHESS_MOVE: "move",
8
+ CHESS_OPPONENT_MOVE: "opponent-move",
9
+ USER_JOINED_ROOM: "user-joined-room",
10
+ USER_RESIGNED: "user-resigned",
11
+ GAME_END: "game-end",
12
+ };
13
+
14
+ module.exports = {
15
+ SOCKET_EVENTS,
16
+ };
backend/dockerfile ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dockerfile for Node Express Backend
2
+
3
+ FROM node
4
+
5
+ # Create App Directory
6
+ RUN mkdir -p /usr/src/app
7
+ WORKDIR /usr/src/app
8
+ RUN npm install -g nodemon
9
+
10
+ # Install Dependencies
11
+ COPY package*.json ./
12
+
13
+ RUN npm install --silent
14
+
15
+ # Copy app source code
16
+ COPY . .
17
+
18
+ RUN chmod +x ./engine/stockfish16
19
+
20
+ # Exports
21
+ EXPOSE 8080
22
+
23
+ CMD ["node","app.js"]
backend/engine/stockfish16 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8f60a016dc767e0d648a8665b8ede3e6e4d28c086ad90517ad26f55b9960bd84
3
+ size 40442144
backend/mail.js ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const nodemailer = require("nodemailer");
2
+ require("dotenv").config();
3
+
4
+ const transporter = nodemailer.createTransport({
5
+ service: "gmail",
6
+ auth: {
7
+ user: process.env.EMAIL_ID,
8
+ pass: process.env.MAIL_SERVER_PASSWORD,
9
+ },
10
+ });
11
+
12
+ const sendEmail = (receiverEmail, subject, data) => {
13
+ let mailDetails = {
14
+ from: process.env.EMAIL_ID,
15
+ to: receiverEmail,
16
+ subject: subject,
17
+ text: data,
18
+ };
19
+ transporter.sendMail(mailDetails, function (err, data) {
20
+ if (err) {
21
+ console.log(err);
22
+ } else {
23
+ console.log("Email sent successfully");
24
+ }
25
+ });
26
+ };
27
+
28
+ module.exports = {
29
+ sendEmail,
30
+ };
backend/models/challenge.js ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const { Schema, default: mongoose } = require("mongoose");
2
+ const { String, ObjectId, Number } = Schema.Types;
3
+
4
+ const challengeSchema = new Schema({
5
+ challenger: {
6
+ type: String,
7
+ required: true,
8
+ },
9
+ challenged: {
10
+ type: String,
11
+ required: true,
12
+ },
13
+ color: {
14
+ type: String,
15
+ enum: ["b", "w"],
16
+ required: true,
17
+ },
18
+ timeLimit: {
19
+ type: Number,
20
+ required: true,
21
+ },
22
+ roomID: {
23
+ type: String,
24
+ required: true,
25
+ // unique: true,
26
+ },
27
+ });
28
+
29
+ const Challenge = mongoose.model("Challenge", challengeSchema);
30
+ module.exports = { Challenge };
backend/models/game.js ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const { Schema, default: mongoose } = require("mongoose");
2
+ const { String, ObjectId, Number } = Schema.Types;
3
+
4
+ const gameSchema = new Schema({
5
+ white: {
6
+ type: ObjectId,
7
+ ref: "User",
8
+ },
9
+ black: {
10
+ type: ObjectId,
11
+ ref: "User",
12
+ },
13
+ timeLimit: {
14
+ type: Number,
15
+ required: true,
16
+ },
17
+ roomID: {
18
+ type: String,
19
+ required: true,
20
+ },
21
+ pgn: {
22
+ type: String,
23
+ required: true,
24
+ },
25
+ winner: {
26
+ type: String,
27
+ enum: ["b", "w", "n"],
28
+ },
29
+ });
30
+
31
+ const Game = mongoose.model("Game", gameSchema);
32
+ module.exports = { Game };
backend/models/user.js ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const { Schema, default: mongoose } = require("mongoose");
2
+ const { String, ObjectId } = Schema.Types;
3
+
4
+ const userSchema = new Schema(
5
+ {
6
+ username: {
7
+ type: String,
8
+ unique: true,
9
+ required: true,
10
+ },
11
+ email: {
12
+ type: String,
13
+ unique: true,
14
+ required: true,
15
+ },
16
+ password_hash: {
17
+ type: String,
18
+ required: true,
19
+ },
20
+ fname: String,
21
+ lname: String,
22
+ location: String,
23
+ country: String,
24
+ friends: [
25
+ {
26
+ type: ObjectId,
27
+ ref: "User",
28
+ },
29
+ ],
30
+ games: [
31
+ {
32
+ type: ObjectId,
33
+ ref: "Game",
34
+ },
35
+ ],
36
+ },
37
+ {
38
+ virtuals: {
39
+ fullName: {
40
+ get() {
41
+ return this.fname + " " + this.lname;
42
+ },
43
+ },
44
+ _friends_: {
45
+ async get() {
46
+ await this.populate("friends", "username email");
47
+ // console.log(this.friends);
48
+ return this.friends.map((friend) => {
49
+ return {
50
+ username: friend.username,
51
+ email: friend.email,
52
+ id: friend.id,
53
+ };
54
+ });
55
+ },
56
+ },
57
+ _games_: {
58
+ async get() {
59
+ await this.populate("games");
60
+ return this.games;
61
+ },
62
+ },
63
+ },
64
+ methods: {
65
+ async getFriends() {
66
+ await this.populate("friends", "username email");
67
+ // console.log(this.friends);
68
+ return this.friends.map((friend) => {
69
+ return {
70
+ username: friend.username,
71
+ email: friend.email,
72
+ id: friend.id,
73
+ };
74
+ });
75
+ },
76
+ async getGames() {
77
+ await this.populate("games");
78
+ return this.games;
79
+ },
80
+ },
81
+ }
82
+ );
83
+
84
+ userSchema.index({ username: "text" });
85
+
86
+ const User = mongoose.model("User", userSchema);
87
+ module.exports = { User };
backend/package-lock.json ADDED
@@ -0,0 +1,1547 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "backend-api",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "backend-api",
9
+ "version": "1.0.0",
10
+ "license": "ISC",
11
+ "dependencies": {
12
+ "bcryptjs": "^2.4.3",
13
+ "body-parser": "^1.20.2",
14
+ "chess.js": "^1.0.0-beta.6",
15
+ "cookie-parser": "^1.4.6",
16
+ "cors": "^2.8.5",
17
+ "dotenv": "^16.3.1",
18
+ "express": "^4.18.2",
19
+ "jsonwebtoken": "^8.5.1",
20
+ "mongoose": "^7.2.1",
21
+ "node-uci": "^1.3.4",
22
+ "nodemailer": "^6.9.3",
23
+ "socket.io": "^4.6.1",
24
+ "uuid": "^9.0.0",
25
+ "zod": "^3.21.4"
26
+ }
27
+ },
28
+ "node_modules/@babel/code-frame": {
29
+ "version": "7.22.13",
30
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
31
+ "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
32
+ "dependencies": {
33
+ "@babel/highlight": "^7.22.13",
34
+ "chalk": "^2.4.2"
35
+ },
36
+ "engines": {
37
+ "node": ">=6.9.0"
38
+ }
39
+ },
40
+ "node_modules/@babel/helper-validator-identifier": {
41
+ "version": "7.22.20",
42
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
43
+ "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
44
+ "engines": {
45
+ "node": ">=6.9.0"
46
+ }
47
+ },
48
+ "node_modules/@babel/highlight": {
49
+ "version": "7.22.20",
50
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
51
+ "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
52
+ "dependencies": {
53
+ "@babel/helper-validator-identifier": "^7.22.20",
54
+ "chalk": "^2.4.2",
55
+ "js-tokens": "^4.0.0"
56
+ },
57
+ "engines": {
58
+ "node": ">=6.9.0"
59
+ }
60
+ },
61
+ "node_modules/@socket.io/component-emitter": {
62
+ "version": "3.1.0",
63
+ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
64
+ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
65
+ },
66
+ "node_modules/@types/cookie": {
67
+ "version": "0.4.1",
68
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
69
+ "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
70
+ },
71
+ "node_modules/@types/cors": {
72
+ "version": "2.8.13",
73
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
74
+ "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
75
+ "dependencies": {
76
+ "@types/node": "*"
77
+ }
78
+ },
79
+ "node_modules/@types/estree": {
80
+ "version": "1.0.5",
81
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
82
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
83
+ "peer": true
84
+ },
85
+ "node_modules/@types/node": {
86
+ "version": "20.2.5",
87
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz",
88
+ "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ=="
89
+ },
90
+ "node_modules/@types/webidl-conversions": {
91
+ "version": "7.0.0",
92
+ "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
93
+ "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog=="
94
+ },
95
+ "node_modules/@types/whatwg-url": {
96
+ "version": "8.2.2",
97
+ "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz",
98
+ "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==",
99
+ "dependencies": {
100
+ "@types/node": "*",
101
+ "@types/webidl-conversions": "*"
102
+ }
103
+ },
104
+ "node_modules/accepts": {
105
+ "version": "1.3.8",
106
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
107
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
108
+ "dependencies": {
109
+ "mime-types": "~2.1.34",
110
+ "negotiator": "0.6.3"
111
+ },
112
+ "engines": {
113
+ "node": ">= 0.6"
114
+ }
115
+ },
116
+ "node_modules/acorn": {
117
+ "version": "7.4.1",
118
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
119
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
120
+ "peer": true,
121
+ "bin": {
122
+ "acorn": "bin/acorn"
123
+ },
124
+ "engines": {
125
+ "node": ">=0.4.0"
126
+ }
127
+ },
128
+ "node_modules/ansi-styles": {
129
+ "version": "3.2.1",
130
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
131
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
132
+ "dependencies": {
133
+ "color-convert": "^1.9.0"
134
+ },
135
+ "engines": {
136
+ "node": ">=4"
137
+ }
138
+ },
139
+ "node_modules/array-flatten": {
140
+ "version": "1.1.1",
141
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
142
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
143
+ },
144
+ "node_modules/base64id": {
145
+ "version": "2.0.0",
146
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
147
+ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
148
+ "engines": {
149
+ "node": "^4.5.0 || >= 5.9"
150
+ }
151
+ },
152
+ "node_modules/bcryptjs": {
153
+ "version": "2.4.3",
154
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
155
+ "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="
156
+ },
157
+ "node_modules/bluebird": {
158
+ "version": "3.7.2",
159
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
160
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
161
+ },
162
+ "node_modules/body-parser": {
163
+ "version": "1.20.2",
164
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
165
+ "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
166
+ "dependencies": {
167
+ "bytes": "3.1.2",
168
+ "content-type": "~1.0.5",
169
+ "debug": "2.6.9",
170
+ "depd": "2.0.0",
171
+ "destroy": "1.2.0",
172
+ "http-errors": "2.0.0",
173
+ "iconv-lite": "0.4.24",
174
+ "on-finished": "2.4.1",
175
+ "qs": "6.11.0",
176
+ "raw-body": "2.5.2",
177
+ "type-is": "~1.6.18",
178
+ "unpipe": "1.0.0"
179
+ },
180
+ "engines": {
181
+ "node": ">= 0.8",
182
+ "npm": "1.2.8000 || >= 1.4.16"
183
+ }
184
+ },
185
+ "node_modules/bson": {
186
+ "version": "5.3.0",
187
+ "resolved": "https://registry.npmjs.org/bson/-/bson-5.3.0.tgz",
188
+ "integrity": "sha512-ukmCZMneMlaC5ebPHXIkP8YJzNl5DC41N5MAIvKDqLggdao342t4McltoJBQfQya/nHBWAcSsYRqlXPoQkTJag==",
189
+ "engines": {
190
+ "node": ">=14.20.1"
191
+ }
192
+ },
193
+ "node_modules/buffer-equal-constant-time": {
194
+ "version": "1.0.1",
195
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
196
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
197
+ },
198
+ "node_modules/buffer-from": {
199
+ "version": "1.1.2",
200
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
201
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
202
+ },
203
+ "node_modules/bytes": {
204
+ "version": "3.1.2",
205
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
206
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
207
+ "engines": {
208
+ "node": ">= 0.8"
209
+ }
210
+ },
211
+ "node_modules/call-bind": {
212
+ "version": "1.0.2",
213
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
214
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
215
+ "dependencies": {
216
+ "function-bind": "^1.1.1",
217
+ "get-intrinsic": "^1.0.2"
218
+ },
219
+ "funding": {
220
+ "url": "https://github.com/sponsors/ljharb"
221
+ }
222
+ },
223
+ "node_modules/chalk": {
224
+ "version": "2.4.2",
225
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
226
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
227
+ "dependencies": {
228
+ "ansi-styles": "^3.2.1",
229
+ "escape-string-regexp": "^1.0.5",
230
+ "supports-color": "^5.3.0"
231
+ },
232
+ "engines": {
233
+ "node": ">=4"
234
+ }
235
+ },
236
+ "node_modules/chess.js": {
237
+ "version": "1.0.0-beta.6",
238
+ "resolved": "https://registry.npmjs.org/chess.js/-/chess.js-1.0.0-beta.6.tgz",
239
+ "integrity": "sha512-sqBfX1VL3csSyqVM5ogbKA+aRlZyWDh276ruWXphwI0lDUMs7iYjZs29BOi49f7mXeunJE7cdfnIZhihsyLnsA=="
240
+ },
241
+ "node_modules/color-convert": {
242
+ "version": "1.9.3",
243
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
244
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
245
+ "dependencies": {
246
+ "color-name": "1.1.3"
247
+ }
248
+ },
249
+ "node_modules/color-name": {
250
+ "version": "1.1.3",
251
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
252
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
253
+ },
254
+ "node_modules/commander": {
255
+ "version": "2.20.3",
256
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
257
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
258
+ },
259
+ "node_modules/content-disposition": {
260
+ "version": "0.5.4",
261
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
262
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
263
+ "dependencies": {
264
+ "safe-buffer": "5.2.1"
265
+ },
266
+ "engines": {
267
+ "node": ">= 0.6"
268
+ }
269
+ },
270
+ "node_modules/content-type": {
271
+ "version": "1.0.5",
272
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
273
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
274
+ "engines": {
275
+ "node": ">= 0.6"
276
+ }
277
+ },
278
+ "node_modules/cookie": {
279
+ "version": "0.5.0",
280
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
281
+ "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
282
+ "engines": {
283
+ "node": ">= 0.6"
284
+ }
285
+ },
286
+ "node_modules/cookie-parser": {
287
+ "version": "1.4.6",
288
+ "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
289
+ "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
290
+ "dependencies": {
291
+ "cookie": "0.4.1",
292
+ "cookie-signature": "1.0.6"
293
+ },
294
+ "engines": {
295
+ "node": ">= 0.8.0"
296
+ }
297
+ },
298
+ "node_modules/cookie-parser/node_modules/cookie": {
299
+ "version": "0.4.1",
300
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
301
+ "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
302
+ "engines": {
303
+ "node": ">= 0.6"
304
+ }
305
+ },
306
+ "node_modules/cookie-signature": {
307
+ "version": "1.0.6",
308
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
309
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
310
+ },
311
+ "node_modules/core-js": {
312
+ "version": "3.6.2",
313
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.2.tgz",
314
+ "integrity": "sha512-hIE5dXkRzRvnZ5vhkRfQxUvDxQZmD9oueA08jDYRBKJHx+VIl/Pne/e0A4x9LObEEthC/TqiZybUoNM4tRgnKg==",
315
+ "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
316
+ "hasInstallScript": true,
317
+ "funding": {
318
+ "type": "opencollective",
319
+ "url": "https://opencollective.com/core-js"
320
+ }
321
+ },
322
+ "node_modules/cors": {
323
+ "version": "2.8.5",
324
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
325
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
326
+ "dependencies": {
327
+ "object-assign": "^4",
328
+ "vary": "^1"
329
+ },
330
+ "engines": {
331
+ "node": ">= 0.10"
332
+ }
333
+ },
334
+ "node_modules/debug": {
335
+ "version": "2.6.9",
336
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
337
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
338
+ "dependencies": {
339
+ "ms": "2.0.0"
340
+ }
341
+ },
342
+ "node_modules/depd": {
343
+ "version": "2.0.0",
344
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
345
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
346
+ "engines": {
347
+ "node": ">= 0.8"
348
+ }
349
+ },
350
+ "node_modules/destroy": {
351
+ "version": "1.2.0",
352
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
353
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
354
+ "engines": {
355
+ "node": ">= 0.8",
356
+ "npm": "1.2.8000 || >= 1.4.16"
357
+ }
358
+ },
359
+ "node_modules/dotenv": {
360
+ "version": "16.3.1",
361
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
362
+ "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
363
+ "engines": {
364
+ "node": ">=12"
365
+ },
366
+ "funding": {
367
+ "url": "https://github.com/motdotla/dotenv?sponsor=1"
368
+ }
369
+ },
370
+ "node_modules/ecdsa-sig-formatter": {
371
+ "version": "1.0.11",
372
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
373
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
374
+ "dependencies": {
375
+ "safe-buffer": "^5.0.1"
376
+ }
377
+ },
378
+ "node_modules/ee-first": {
379
+ "version": "1.1.1",
380
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
381
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
382
+ },
383
+ "node_modules/encodeurl": {
384
+ "version": "1.0.2",
385
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
386
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
387
+ "engines": {
388
+ "node": ">= 0.8"
389
+ }
390
+ },
391
+ "node_modules/engine.io": {
392
+ "version": "6.4.2",
393
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz",
394
+ "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==",
395
+ "dependencies": {
396
+ "@types/cookie": "^0.4.1",
397
+ "@types/cors": "^2.8.12",
398
+ "@types/node": ">=10.0.0",
399
+ "accepts": "~1.3.4",
400
+ "base64id": "2.0.0",
401
+ "cookie": "~0.4.1",
402
+ "cors": "~2.8.5",
403
+ "debug": "~4.3.1",
404
+ "engine.io-parser": "~5.0.3",
405
+ "ws": "~8.11.0"
406
+ },
407
+ "engines": {
408
+ "node": ">=10.0.0"
409
+ }
410
+ },
411
+ "node_modules/engine.io-parser": {
412
+ "version": "5.0.7",
413
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.7.tgz",
414
+ "integrity": "sha512-P+jDFbvK6lE3n1OL+q9KuzdOFWkkZ/cMV9gol/SbVfpyqfvrfrFTOFJ6fQm2VC3PZHlU3QPhVwmbsCnauHF2MQ==",
415
+ "engines": {
416
+ "node": ">=10.0.0"
417
+ }
418
+ },
419
+ "node_modules/engine.io/node_modules/cookie": {
420
+ "version": "0.4.2",
421
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
422
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
423
+ "engines": {
424
+ "node": ">= 0.6"
425
+ }
426
+ },
427
+ "node_modules/engine.io/node_modules/debug": {
428
+ "version": "4.3.4",
429
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
430
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
431
+ "dependencies": {
432
+ "ms": "2.1.2"
433
+ },
434
+ "engines": {
435
+ "node": ">=6.0"
436
+ },
437
+ "peerDependenciesMeta": {
438
+ "supports-color": {
439
+ "optional": true
440
+ }
441
+ }
442
+ },
443
+ "node_modules/engine.io/node_modules/ms": {
444
+ "version": "2.1.2",
445
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
446
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
447
+ },
448
+ "node_modules/escape-html": {
449
+ "version": "1.0.3",
450
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
451
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
452
+ },
453
+ "node_modules/escape-string-regexp": {
454
+ "version": "1.0.5",
455
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
456
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
457
+ "engines": {
458
+ "node": ">=0.8.0"
459
+ }
460
+ },
461
+ "node_modules/estree-walker": {
462
+ "version": "0.6.1",
463
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
464
+ "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="
465
+ },
466
+ "node_modules/etag": {
467
+ "version": "1.8.1",
468
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
469
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
470
+ "engines": {
471
+ "node": ">= 0.6"
472
+ }
473
+ },
474
+ "node_modules/express": {
475
+ "version": "4.18.2",
476
+ "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
477
+ "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
478
+ "dependencies": {
479
+ "accepts": "~1.3.8",
480
+ "array-flatten": "1.1.1",
481
+ "body-parser": "1.20.1",
482
+ "content-disposition": "0.5.4",
483
+ "content-type": "~1.0.4",
484
+ "cookie": "0.5.0",
485
+ "cookie-signature": "1.0.6",
486
+ "debug": "2.6.9",
487
+ "depd": "2.0.0",
488
+ "encodeurl": "~1.0.2",
489
+ "escape-html": "~1.0.3",
490
+ "etag": "~1.8.1",
491
+ "finalhandler": "1.2.0",
492
+ "fresh": "0.5.2",
493
+ "http-errors": "2.0.0",
494
+ "merge-descriptors": "1.0.1",
495
+ "methods": "~1.1.2",
496
+ "on-finished": "2.4.1",
497
+ "parseurl": "~1.3.3",
498
+ "path-to-regexp": "0.1.7",
499
+ "proxy-addr": "~2.0.7",
500
+ "qs": "6.11.0",
501
+ "range-parser": "~1.2.1",
502
+ "safe-buffer": "5.2.1",
503
+ "send": "0.18.0",
504
+ "serve-static": "1.15.0",
505
+ "setprototypeof": "1.2.0",
506
+ "statuses": "2.0.1",
507
+ "type-is": "~1.6.18",
508
+ "utils-merge": "1.0.1",
509
+ "vary": "~1.1.2"
510
+ },
511
+ "engines": {
512
+ "node": ">= 0.10.0"
513
+ }
514
+ },
515
+ "node_modules/express/node_modules/body-parser": {
516
+ "version": "1.20.1",
517
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
518
+ "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
519
+ "dependencies": {
520
+ "bytes": "3.1.2",
521
+ "content-type": "~1.0.4",
522
+ "debug": "2.6.9",
523
+ "depd": "2.0.0",
524
+ "destroy": "1.2.0",
525
+ "http-errors": "2.0.0",
526
+ "iconv-lite": "0.4.24",
527
+ "on-finished": "2.4.1",
528
+ "qs": "6.11.0",
529
+ "raw-body": "2.5.1",
530
+ "type-is": "~1.6.18",
531
+ "unpipe": "1.0.0"
532
+ },
533
+ "engines": {
534
+ "node": ">= 0.8",
535
+ "npm": "1.2.8000 || >= 1.4.16"
536
+ }
537
+ },
538
+ "node_modules/express/node_modules/raw-body": {
539
+ "version": "2.5.1",
540
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
541
+ "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
542
+ "dependencies": {
543
+ "bytes": "3.1.2",
544
+ "http-errors": "2.0.0",
545
+ "iconv-lite": "0.4.24",
546
+ "unpipe": "1.0.0"
547
+ },
548
+ "engines": {
549
+ "node": ">= 0.8"
550
+ }
551
+ },
552
+ "node_modules/finalhandler": {
553
+ "version": "1.2.0",
554
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
555
+ "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
556
+ "dependencies": {
557
+ "debug": "2.6.9",
558
+ "encodeurl": "~1.0.2",
559
+ "escape-html": "~1.0.3",
560
+ "on-finished": "2.4.1",
561
+ "parseurl": "~1.3.3",
562
+ "statuses": "2.0.1",
563
+ "unpipe": "~1.0.0"
564
+ },
565
+ "engines": {
566
+ "node": ">= 0.8"
567
+ }
568
+ },
569
+ "node_modules/forwarded": {
570
+ "version": "0.2.0",
571
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
572
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
573
+ "engines": {
574
+ "node": ">= 0.6"
575
+ }
576
+ },
577
+ "node_modules/fresh": {
578
+ "version": "0.5.2",
579
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
580
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
581
+ "engines": {
582
+ "node": ">= 0.6"
583
+ }
584
+ },
585
+ "node_modules/function-bind": {
586
+ "version": "1.1.1",
587
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
588
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
589
+ },
590
+ "node_modules/get-intrinsic": {
591
+ "version": "1.2.1",
592
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
593
+ "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
594
+ "dependencies": {
595
+ "function-bind": "^1.1.1",
596
+ "has": "^1.0.3",
597
+ "has-proto": "^1.0.1",
598
+ "has-symbols": "^1.0.3"
599
+ },
600
+ "funding": {
601
+ "url": "https://github.com/sponsors/ljharb"
602
+ }
603
+ },
604
+ "node_modules/has": {
605
+ "version": "1.0.3",
606
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
607
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
608
+ "dependencies": {
609
+ "function-bind": "^1.1.1"
610
+ },
611
+ "engines": {
612
+ "node": ">= 0.4.0"
613
+ }
614
+ },
615
+ "node_modules/has-flag": {
616
+ "version": "3.0.0",
617
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
618
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
619
+ "engines": {
620
+ "node": ">=4"
621
+ }
622
+ },
623
+ "node_modules/has-proto": {
624
+ "version": "1.0.1",
625
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
626
+ "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
627
+ "engines": {
628
+ "node": ">= 0.4"
629
+ },
630
+ "funding": {
631
+ "url": "https://github.com/sponsors/ljharb"
632
+ }
633
+ },
634
+ "node_modules/has-symbols": {
635
+ "version": "1.0.3",
636
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
637
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
638
+ "engines": {
639
+ "node": ">= 0.4"
640
+ },
641
+ "funding": {
642
+ "url": "https://github.com/sponsors/ljharb"
643
+ }
644
+ },
645
+ "node_modules/http-errors": {
646
+ "version": "2.0.0",
647
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
648
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
649
+ "dependencies": {
650
+ "depd": "2.0.0",
651
+ "inherits": "2.0.4",
652
+ "setprototypeof": "1.2.0",
653
+ "statuses": "2.0.1",
654
+ "toidentifier": "1.0.1"
655
+ },
656
+ "engines": {
657
+ "node": ">= 0.8"
658
+ }
659
+ },
660
+ "node_modules/iconv-lite": {
661
+ "version": "0.4.24",
662
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
663
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
664
+ "dependencies": {
665
+ "safer-buffer": ">= 2.1.2 < 3"
666
+ },
667
+ "engines": {
668
+ "node": ">=0.10.0"
669
+ }
670
+ },
671
+ "node_modules/inherits": {
672
+ "version": "2.0.4",
673
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
674
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
675
+ },
676
+ "node_modules/ip": {
677
+ "version": "2.0.0",
678
+ "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
679
+ "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
680
+ },
681
+ "node_modules/ipaddr.js": {
682
+ "version": "1.9.1",
683
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
684
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
685
+ "engines": {
686
+ "node": ">= 0.10"
687
+ }
688
+ },
689
+ "node_modules/jest-worker": {
690
+ "version": "24.9.0",
691
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz",
692
+ "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==",
693
+ "dependencies": {
694
+ "merge-stream": "^2.0.0",
695
+ "supports-color": "^6.1.0"
696
+ },
697
+ "engines": {
698
+ "node": ">= 6"
699
+ }
700
+ },
701
+ "node_modules/jest-worker/node_modules/supports-color": {
702
+ "version": "6.1.0",
703
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
704
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
705
+ "dependencies": {
706
+ "has-flag": "^3.0.0"
707
+ },
708
+ "engines": {
709
+ "node": ">=6"
710
+ }
711
+ },
712
+ "node_modules/js-tokens": {
713
+ "version": "4.0.0",
714
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
715
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
716
+ },
717
+ "node_modules/jsonwebtoken": {
718
+ "version": "8.5.1",
719
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
720
+ "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
721
+ "dependencies": {
722
+ "jws": "^3.2.2",
723
+ "lodash.includes": "^4.3.0",
724
+ "lodash.isboolean": "^3.0.3",
725
+ "lodash.isinteger": "^4.0.4",
726
+ "lodash.isnumber": "^3.0.3",
727
+ "lodash.isplainobject": "^4.0.6",
728
+ "lodash.isstring": "^4.0.1",
729
+ "lodash.once": "^4.0.0",
730
+ "ms": "^2.1.1",
731
+ "semver": "^5.6.0"
732
+ },
733
+ "engines": {
734
+ "node": ">=4",
735
+ "npm": ">=1.4.28"
736
+ }
737
+ },
738
+ "node_modules/jsonwebtoken/node_modules/ms": {
739
+ "version": "2.1.3",
740
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
741
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
742
+ },
743
+ "node_modules/jwa": {
744
+ "version": "1.4.1",
745
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
746
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
747
+ "dependencies": {
748
+ "buffer-equal-constant-time": "1.0.1",
749
+ "ecdsa-sig-formatter": "1.0.11",
750
+ "safe-buffer": "^5.0.1"
751
+ }
752
+ },
753
+ "node_modules/jws": {
754
+ "version": "3.2.2",
755
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
756
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
757
+ "dependencies": {
758
+ "jwa": "^1.4.1",
759
+ "safe-buffer": "^5.0.1"
760
+ }
761
+ },
762
+ "node_modules/kareem": {
763
+ "version": "2.5.1",
764
+ "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz",
765
+ "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==",
766
+ "engines": {
767
+ "node": ">=12.0.0"
768
+ }
769
+ },
770
+ "node_modules/lodash": {
771
+ "version": "4.17.15",
772
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
773
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
774
+ },
775
+ "node_modules/lodash.includes": {
776
+ "version": "4.3.0",
777
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
778
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
779
+ },
780
+ "node_modules/lodash.isboolean": {
781
+ "version": "3.0.3",
782
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
783
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
784
+ },
785
+ "node_modules/lodash.isinteger": {
786
+ "version": "4.0.4",
787
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
788
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
789
+ },
790
+ "node_modules/lodash.isnumber": {
791
+ "version": "3.0.3",
792
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
793
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
794
+ },
795
+ "node_modules/lodash.isplainobject": {
796
+ "version": "4.0.6",
797
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
798
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
799
+ },
800
+ "node_modules/lodash.isstring": {
801
+ "version": "4.0.1",
802
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
803
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
804
+ },
805
+ "node_modules/lodash.once": {
806
+ "version": "4.1.1",
807
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
808
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
809
+ },
810
+ "node_modules/media-typer": {
811
+ "version": "0.3.0",
812
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
813
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
814
+ "engines": {
815
+ "node": ">= 0.6"
816
+ }
817
+ },
818
+ "node_modules/memory-pager": {
819
+ "version": "1.5.0",
820
+ "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
821
+ "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
822
+ "optional": true
823
+ },
824
+ "node_modules/merge-descriptors": {
825
+ "version": "1.0.1",
826
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
827
+ "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
828
+ },
829
+ "node_modules/merge-stream": {
830
+ "version": "2.0.0",
831
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
832
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
833
+ },
834
+ "node_modules/methods": {
835
+ "version": "1.1.2",
836
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
837
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
838
+ "engines": {
839
+ "node": ">= 0.6"
840
+ }
841
+ },
842
+ "node_modules/mime": {
843
+ "version": "1.6.0",
844
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
845
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
846
+ "bin": {
847
+ "mime": "cli.js"
848
+ },
849
+ "engines": {
850
+ "node": ">=4"
851
+ }
852
+ },
853
+ "node_modules/mime-db": {
854
+ "version": "1.52.0",
855
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
856
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
857
+ "engines": {
858
+ "node": ">= 0.6"
859
+ }
860
+ },
861
+ "node_modules/mime-types": {
862
+ "version": "2.1.35",
863
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
864
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
865
+ "dependencies": {
866
+ "mime-db": "1.52.0"
867
+ },
868
+ "engines": {
869
+ "node": ">= 0.6"
870
+ }
871
+ },
872
+ "node_modules/mongodb": {
873
+ "version": "5.5.0",
874
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.5.0.tgz",
875
+ "integrity": "sha512-XgrkUgAAdfnZKQfk5AsYL8j7O99WHd4YXPxYxnh8dZxD+ekYWFRA3JktUsBnfg+455Smf75/+asoU/YLwNGoQQ==",
876
+ "dependencies": {
877
+ "bson": "^5.3.0",
878
+ "mongodb-connection-string-url": "^2.6.0",
879
+ "socks": "^2.7.1"
880
+ },
881
+ "engines": {
882
+ "node": ">=14.20.1"
883
+ },
884
+ "optionalDependencies": {
885
+ "saslprep": "^1.0.3"
886
+ },
887
+ "peerDependencies": {
888
+ "@aws-sdk/credential-providers": "^3.201.0",
889
+ "mongodb-client-encryption": ">=2.3.0 <3",
890
+ "snappy": "^7.2.2"
891
+ },
892
+ "peerDependenciesMeta": {
893
+ "@aws-sdk/credential-providers": {
894
+ "optional": true
895
+ },
896
+ "mongodb-client-encryption": {
897
+ "optional": true
898
+ },
899
+ "snappy": {
900
+ "optional": true
901
+ }
902
+ }
903
+ },
904
+ "node_modules/mongodb-connection-string-url": {
905
+ "version": "2.6.0",
906
+ "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz",
907
+ "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==",
908
+ "dependencies": {
909
+ "@types/whatwg-url": "^8.2.1",
910
+ "whatwg-url": "^11.0.0"
911
+ }
912
+ },
913
+ "node_modules/mongoose": {
914
+ "version": "7.2.1",
915
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.2.1.tgz",
916
+ "integrity": "sha512-c2OOl+ch9NlmPeJw7UjSb2jHNjoOw1XXHyzwygIf4z1GmaBx1OYb8OYqHkYPivvEmfY/vUWZFCgePsDqZgFn2w==",
917
+ "dependencies": {
918
+ "bson": "^5.3.0",
919
+ "kareem": "2.5.1",
920
+ "mongodb": "5.5.0",
921
+ "mpath": "0.9.0",
922
+ "mquery": "5.0.0",
923
+ "ms": "2.1.3",
924
+ "sift": "16.0.1"
925
+ },
926
+ "engines": {
927
+ "node": ">=14.20.1"
928
+ },
929
+ "funding": {
930
+ "type": "opencollective",
931
+ "url": "https://opencollective.com/mongoose"
932
+ }
933
+ },
934
+ "node_modules/mongoose/node_modules/ms": {
935
+ "version": "2.1.3",
936
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
937
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
938
+ },
939
+ "node_modules/mpath": {
940
+ "version": "0.9.0",
941
+ "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
942
+ "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
943
+ "engines": {
944
+ "node": ">=4.0.0"
945
+ }
946
+ },
947
+ "node_modules/mquery": {
948
+ "version": "5.0.0",
949
+ "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz",
950
+ "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==",
951
+ "dependencies": {
952
+ "debug": "4.x"
953
+ },
954
+ "engines": {
955
+ "node": ">=14.0.0"
956
+ }
957
+ },
958
+ "node_modules/mquery/node_modules/debug": {
959
+ "version": "4.3.4",
960
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
961
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
962
+ "dependencies": {
963
+ "ms": "2.1.2"
964
+ },
965
+ "engines": {
966
+ "node": ">=6.0"
967
+ },
968
+ "peerDependenciesMeta": {
969
+ "supports-color": {
970
+ "optional": true
971
+ }
972
+ }
973
+ },
974
+ "node_modules/mquery/node_modules/ms": {
975
+ "version": "2.1.2",
976
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
977
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
978
+ },
979
+ "node_modules/ms": {
980
+ "version": "2.0.0",
981
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
982
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
983
+ },
984
+ "node_modules/negotiator": {
985
+ "version": "0.6.3",
986
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
987
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
988
+ "engines": {
989
+ "node": ">= 0.6"
990
+ }
991
+ },
992
+ "node_modules/node-uci": {
993
+ "version": "1.3.4",
994
+ "resolved": "https://registry.npmjs.org/node-uci/-/node-uci-1.3.4.tgz",
995
+ "integrity": "sha512-WeFuzceyHUniQO4XpKHjKFV4R+S63aJ/B/6kMO5ULh4KDeNbsBhzhtuuTNOczyy7dYwsjSoHyJjEocdVaecrBg==",
996
+ "dependencies": {
997
+ "bluebird": "3.7.2",
998
+ "core-js": "3.6.2",
999
+ "debug": "4.1.1",
1000
+ "lodash": "4.17.15",
1001
+ "rollup-plugin-terser": "5.1.3"
1002
+ }
1003
+ },
1004
+ "node_modules/node-uci/node_modules/debug": {
1005
+ "version": "4.1.1",
1006
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
1007
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
1008
+ "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
1009
+ "dependencies": {
1010
+ "ms": "^2.1.1"
1011
+ }
1012
+ },
1013
+ "node_modules/node-uci/node_modules/ms": {
1014
+ "version": "2.1.3",
1015
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1016
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1017
+ },
1018
+ "node_modules/nodemailer": {
1019
+ "version": "6.9.3",
1020
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.3.tgz",
1021
+ "integrity": "sha512-fy9v3NgTzBngrMFkDsKEj0r02U7jm6XfC3b52eoNV+GCrGj+s8pt5OqhiJdWKuw51zCTdiNR/IUD1z33LIIGpg==",
1022
+ "engines": {
1023
+ "node": ">=6.0.0"
1024
+ }
1025
+ },
1026
+ "node_modules/object-assign": {
1027
+ "version": "4.1.1",
1028
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1029
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
1030
+ "engines": {
1031
+ "node": ">=0.10.0"
1032
+ }
1033
+ },
1034
+ "node_modules/object-inspect": {
1035
+ "version": "1.12.3",
1036
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
1037
+ "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
1038
+ "funding": {
1039
+ "url": "https://github.com/sponsors/ljharb"
1040
+ }
1041
+ },
1042
+ "node_modules/on-finished": {
1043
+ "version": "2.4.1",
1044
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
1045
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
1046
+ "dependencies": {
1047
+ "ee-first": "1.1.1"
1048
+ },
1049
+ "engines": {
1050
+ "node": ">= 0.8"
1051
+ }
1052
+ },
1053
+ "node_modules/parseurl": {
1054
+ "version": "1.3.3",
1055
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1056
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
1057
+ "engines": {
1058
+ "node": ">= 0.8"
1059
+ }
1060
+ },
1061
+ "node_modules/path-to-regexp": {
1062
+ "version": "0.1.7",
1063
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1064
+ "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
1065
+ },
1066
+ "node_modules/proxy-addr": {
1067
+ "version": "2.0.7",
1068
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
1069
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1070
+ "dependencies": {
1071
+ "forwarded": "0.2.0",
1072
+ "ipaddr.js": "1.9.1"
1073
+ },
1074
+ "engines": {
1075
+ "node": ">= 0.10"
1076
+ }
1077
+ },
1078
+ "node_modules/punycode": {
1079
+ "version": "2.3.0",
1080
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
1081
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
1082
+ "engines": {
1083
+ "node": ">=6"
1084
+ }
1085
+ },
1086
+ "node_modules/qs": {
1087
+ "version": "6.11.0",
1088
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
1089
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
1090
+ "dependencies": {
1091
+ "side-channel": "^1.0.4"
1092
+ },
1093
+ "engines": {
1094
+ "node": ">=0.6"
1095
+ },
1096
+ "funding": {
1097
+ "url": "https://github.com/sponsors/ljharb"
1098
+ }
1099
+ },
1100
+ "node_modules/range-parser": {
1101
+ "version": "1.2.1",
1102
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1103
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1104
+ "engines": {
1105
+ "node": ">= 0.6"
1106
+ }
1107
+ },
1108
+ "node_modules/raw-body": {
1109
+ "version": "2.5.2",
1110
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
1111
+ "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
1112
+ "dependencies": {
1113
+ "bytes": "3.1.2",
1114
+ "http-errors": "2.0.0",
1115
+ "iconv-lite": "0.4.24",
1116
+ "unpipe": "1.0.0"
1117
+ },
1118
+ "engines": {
1119
+ "node": ">= 0.8"
1120
+ }
1121
+ },
1122
+ "node_modules/rollup": {
1123
+ "version": "1.32.1",
1124
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz",
1125
+ "integrity": "sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==",
1126
+ "peer": true,
1127
+ "dependencies": {
1128
+ "@types/estree": "*",
1129
+ "@types/node": "*",
1130
+ "acorn": "^7.1.0"
1131
+ },
1132
+ "bin": {
1133
+ "rollup": "dist/bin/rollup"
1134
+ }
1135
+ },
1136
+ "node_modules/rollup-plugin-terser": {
1137
+ "version": "5.1.3",
1138
+ "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-5.1.3.tgz",
1139
+ "integrity": "sha512-FuFuXE5QUJ7snyxHLPp/0LFXJhdomKlIx/aK7Tg88Yubsx/UU/lmInoJafXJ4jwVVNcORJ1wRUC5T9cy5yk0wA==",
1140
+ "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser",
1141
+ "dependencies": {
1142
+ "@babel/code-frame": "^7.0.0",
1143
+ "jest-worker": "^24.6.0",
1144
+ "rollup-pluginutils": "^2.8.1",
1145
+ "serialize-javascript": "^2.1.2",
1146
+ "terser": "^4.1.0"
1147
+ },
1148
+ "peerDependencies": {
1149
+ "rollup": ">=0.66.0 <2"
1150
+ }
1151
+ },
1152
+ "node_modules/rollup-pluginutils": {
1153
+ "version": "2.8.2",
1154
+ "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz",
1155
+ "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==",
1156
+ "dependencies": {
1157
+ "estree-walker": "^0.6.1"
1158
+ }
1159
+ },
1160
+ "node_modules/safe-buffer": {
1161
+ "version": "5.2.1",
1162
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1163
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1164
+ "funding": [
1165
+ {
1166
+ "type": "github",
1167
+ "url": "https://github.com/sponsors/feross"
1168
+ },
1169
+ {
1170
+ "type": "patreon",
1171
+ "url": "https://www.patreon.com/feross"
1172
+ },
1173
+ {
1174
+ "type": "consulting",
1175
+ "url": "https://feross.org/support"
1176
+ }
1177
+ ]
1178
+ },
1179
+ "node_modules/safer-buffer": {
1180
+ "version": "2.1.2",
1181
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1182
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1183
+ },
1184
+ "node_modules/saslprep": {
1185
+ "version": "1.0.3",
1186
+ "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
1187
+ "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
1188
+ "optional": true,
1189
+ "dependencies": {
1190
+ "sparse-bitfield": "^3.0.3"
1191
+ },
1192
+ "engines": {
1193
+ "node": ">=6"
1194
+ }
1195
+ },
1196
+ "node_modules/semver": {
1197
+ "version": "5.7.1",
1198
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
1199
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
1200
+ "bin": {
1201
+ "semver": "bin/semver"
1202
+ }
1203
+ },
1204
+ "node_modules/send": {
1205
+ "version": "0.18.0",
1206
+ "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
1207
+ "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
1208
+ "dependencies": {
1209
+ "debug": "2.6.9",
1210
+ "depd": "2.0.0",
1211
+ "destroy": "1.2.0",
1212
+ "encodeurl": "~1.0.2",
1213
+ "escape-html": "~1.0.3",
1214
+ "etag": "~1.8.1",
1215
+ "fresh": "0.5.2",
1216
+ "http-errors": "2.0.0",
1217
+ "mime": "1.6.0",
1218
+ "ms": "2.1.3",
1219
+ "on-finished": "2.4.1",
1220
+ "range-parser": "~1.2.1",
1221
+ "statuses": "2.0.1"
1222
+ },
1223
+ "engines": {
1224
+ "node": ">= 0.8.0"
1225
+ }
1226
+ },
1227
+ "node_modules/send/node_modules/ms": {
1228
+ "version": "2.1.3",
1229
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1230
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1231
+ },
1232
+ "node_modules/serialize-javascript": {
1233
+ "version": "2.1.2",
1234
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
1235
+ "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ=="
1236
+ },
1237
+ "node_modules/serve-static": {
1238
+ "version": "1.15.0",
1239
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
1240
+ "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
1241
+ "dependencies": {
1242
+ "encodeurl": "~1.0.2",
1243
+ "escape-html": "~1.0.3",
1244
+ "parseurl": "~1.3.3",
1245
+ "send": "0.18.0"
1246
+ },
1247
+ "engines": {
1248
+ "node": ">= 0.8.0"
1249
+ }
1250
+ },
1251
+ "node_modules/setprototypeof": {
1252
+ "version": "1.2.0",
1253
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1254
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
1255
+ },
1256
+ "node_modules/side-channel": {
1257
+ "version": "1.0.4",
1258
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
1259
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
1260
+ "dependencies": {
1261
+ "call-bind": "^1.0.0",
1262
+ "get-intrinsic": "^1.0.2",
1263
+ "object-inspect": "^1.9.0"
1264
+ },
1265
+ "funding": {
1266
+ "url": "https://github.com/sponsors/ljharb"
1267
+ }
1268
+ },
1269
+ "node_modules/sift": {
1270
+ "version": "16.0.1",
1271
+ "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz",
1272
+ "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ=="
1273
+ },
1274
+ "node_modules/smart-buffer": {
1275
+ "version": "4.2.0",
1276
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
1277
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
1278
+ "engines": {
1279
+ "node": ">= 6.0.0",
1280
+ "npm": ">= 3.0.0"
1281
+ }
1282
+ },
1283
+ "node_modules/socket.io": {
1284
+ "version": "4.6.1",
1285
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz",
1286
+ "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==",
1287
+ "dependencies": {
1288
+ "accepts": "~1.3.4",
1289
+ "base64id": "~2.0.0",
1290
+ "debug": "~4.3.2",
1291
+ "engine.io": "~6.4.1",
1292
+ "socket.io-adapter": "~2.5.2",
1293
+ "socket.io-parser": "~4.2.1"
1294
+ },
1295
+ "engines": {
1296
+ "node": ">=10.0.0"
1297
+ }
1298
+ },
1299
+ "node_modules/socket.io-adapter": {
1300
+ "version": "2.5.2",
1301
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz",
1302
+ "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==",
1303
+ "dependencies": {
1304
+ "ws": "~8.11.0"
1305
+ }
1306
+ },
1307
+ "node_modules/socket.io-parser": {
1308
+ "version": "4.2.3",
1309
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz",
1310
+ "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==",
1311
+ "dependencies": {
1312
+ "@socket.io/component-emitter": "~3.1.0",
1313
+ "debug": "~4.3.1"
1314
+ },
1315
+ "engines": {
1316
+ "node": ">=10.0.0"
1317
+ }
1318
+ },
1319
+ "node_modules/socket.io-parser/node_modules/debug": {
1320
+ "version": "4.3.4",
1321
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
1322
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
1323
+ "dependencies": {
1324
+ "ms": "2.1.2"
1325
+ },
1326
+ "engines": {
1327
+ "node": ">=6.0"
1328
+ },
1329
+ "peerDependenciesMeta": {
1330
+ "supports-color": {
1331
+ "optional": true
1332
+ }
1333
+ }
1334
+ },
1335
+ "node_modules/socket.io-parser/node_modules/ms": {
1336
+ "version": "2.1.2",
1337
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1338
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
1339
+ },
1340
+ "node_modules/socket.io/node_modules/debug": {
1341
+ "version": "4.3.4",
1342
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
1343
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
1344
+ "dependencies": {
1345
+ "ms": "2.1.2"
1346
+ },
1347
+ "engines": {
1348
+ "node": ">=6.0"
1349
+ },
1350
+ "peerDependenciesMeta": {
1351
+ "supports-color": {
1352
+ "optional": true
1353
+ }
1354
+ }
1355
+ },
1356
+ "node_modules/socket.io/node_modules/ms": {
1357
+ "version": "2.1.2",
1358
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1359
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
1360
+ },
1361
+ "node_modules/socks": {
1362
+ "version": "2.7.1",
1363
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
1364
+ "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
1365
+ "dependencies": {
1366
+ "ip": "^2.0.0",
1367
+ "smart-buffer": "^4.2.0"
1368
+ },
1369
+ "engines": {
1370
+ "node": ">= 10.13.0",
1371
+ "npm": ">= 3.0.0"
1372
+ }
1373
+ },
1374
+ "node_modules/source-map": {
1375
+ "version": "0.6.1",
1376
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
1377
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
1378
+ "engines": {
1379
+ "node": ">=0.10.0"
1380
+ }
1381
+ },
1382
+ "node_modules/source-map-support": {
1383
+ "version": "0.5.21",
1384
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
1385
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
1386
+ "dependencies": {
1387
+ "buffer-from": "^1.0.0",
1388
+ "source-map": "^0.6.0"
1389
+ }
1390
+ },
1391
+ "node_modules/sparse-bitfield": {
1392
+ "version": "3.0.3",
1393
+ "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
1394
+ "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
1395
+ "optional": true,
1396
+ "dependencies": {
1397
+ "memory-pager": "^1.0.2"
1398
+ }
1399
+ },
1400
+ "node_modules/statuses": {
1401
+ "version": "2.0.1",
1402
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1403
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1404
+ "engines": {
1405
+ "node": ">= 0.8"
1406
+ }
1407
+ },
1408
+ "node_modules/supports-color": {
1409
+ "version": "5.5.0",
1410
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
1411
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1412
+ "dependencies": {
1413
+ "has-flag": "^3.0.0"
1414
+ },
1415
+ "engines": {
1416
+ "node": ">=4"
1417
+ }
1418
+ },
1419
+ "node_modules/terser": {
1420
+ "version": "4.8.1",
1421
+ "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz",
1422
+ "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==",
1423
+ "dependencies": {
1424
+ "commander": "^2.20.0",
1425
+ "source-map": "~0.6.1",
1426
+ "source-map-support": "~0.5.12"
1427
+ },
1428
+ "bin": {
1429
+ "terser": "bin/terser"
1430
+ },
1431
+ "engines": {
1432
+ "node": ">=6.0.0"
1433
+ }
1434
+ },
1435
+ "node_modules/toidentifier": {
1436
+ "version": "1.0.1",
1437
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1438
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1439
+ "engines": {
1440
+ "node": ">=0.6"
1441
+ }
1442
+ },
1443
+ "node_modules/tr46": {
1444
+ "version": "3.0.0",
1445
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
1446
+ "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
1447
+ "dependencies": {
1448
+ "punycode": "^2.1.1"
1449
+ },
1450
+ "engines": {
1451
+ "node": ">=12"
1452
+ }
1453
+ },
1454
+ "node_modules/type-is": {
1455
+ "version": "1.6.18",
1456
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1457
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1458
+ "dependencies": {
1459
+ "media-typer": "0.3.0",
1460
+ "mime-types": "~2.1.24"
1461
+ },
1462
+ "engines": {
1463
+ "node": ">= 0.6"
1464
+ }
1465
+ },
1466
+ "node_modules/unpipe": {
1467
+ "version": "1.0.0",
1468
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1469
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1470
+ "engines": {
1471
+ "node": ">= 0.8"
1472
+ }
1473
+ },
1474
+ "node_modules/utils-merge": {
1475
+ "version": "1.0.1",
1476
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1477
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
1478
+ "engines": {
1479
+ "node": ">= 0.4.0"
1480
+ }
1481
+ },
1482
+ "node_modules/uuid": {
1483
+ "version": "9.0.0",
1484
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
1485
+ "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
1486
+ "bin": {
1487
+ "uuid": "dist/bin/uuid"
1488
+ }
1489
+ },
1490
+ "node_modules/vary": {
1491
+ "version": "1.1.2",
1492
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1493
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1494
+ "engines": {
1495
+ "node": ">= 0.8"
1496
+ }
1497
+ },
1498
+ "node_modules/webidl-conversions": {
1499
+ "version": "7.0.0",
1500
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
1501
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
1502
+ "engines": {
1503
+ "node": ">=12"
1504
+ }
1505
+ },
1506
+ "node_modules/whatwg-url": {
1507
+ "version": "11.0.0",
1508
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
1509
+ "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
1510
+ "dependencies": {
1511
+ "tr46": "^3.0.0",
1512
+ "webidl-conversions": "^7.0.0"
1513
+ },
1514
+ "engines": {
1515
+ "node": ">=12"
1516
+ }
1517
+ },
1518
+ "node_modules/ws": {
1519
+ "version": "8.11.0",
1520
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
1521
+ "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
1522
+ "engines": {
1523
+ "node": ">=10.0.0"
1524
+ },
1525
+ "peerDependencies": {
1526
+ "bufferutil": "^4.0.1",
1527
+ "utf-8-validate": "^5.0.2"
1528
+ },
1529
+ "peerDependenciesMeta": {
1530
+ "bufferutil": {
1531
+ "optional": true
1532
+ },
1533
+ "utf-8-validate": {
1534
+ "optional": true
1535
+ }
1536
+ }
1537
+ },
1538
+ "node_modules/zod": {
1539
+ "version": "3.21.4",
1540
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",
1541
+ "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==",
1542
+ "funding": {
1543
+ "url": "https://github.com/sponsors/colinhacks"
1544
+ }
1545
+ }
1546
+ }
1547
+ }
backend/package.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "backend-api",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "start": "export MODE=PROD && node app.js",
8
+ "dev": "export MODE=DEV && nodemon app.js"
9
+ },
10
+ "keywords": [],
11
+ "author": "",
12
+ "license": "ISC",
13
+ "dependencies": {
14
+ "bcryptjs": "^2.4.3",
15
+ "body-parser": "^1.20.2",
16
+ "chess.js": "^1.0.0-beta.6",
17
+ "cookie-parser": "^1.4.6",
18
+ "cors": "^2.8.5",
19
+ "dotenv": "^16.3.1",
20
+ "express": "^4.18.2",
21
+ "jsonwebtoken": "^8.5.1",
22
+ "mongoose": "^7.2.1",
23
+ "node-uci": "^1.3.4",
24
+ "nodemailer": "^6.9.3",
25
+ "socket.io": "^4.6.1",
26
+ "uuid": "^9.0.0",
27
+ "zod": "^3.21.4"
28
+ }
29
+ }
backend/routes/auth.js ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require("express");
2
+ const {
3
+ createJSONToken,
4
+ isValidPassword,
5
+ validateJSONToken,
6
+ generatePasswordHash,
7
+ checkAuth,
8
+ } = require("../util/auth");
9
+ const { isValidEmail, isValidText } = require("../util/validation");
10
+ const { User } = require("../models/user");
11
+ const { z, ZodError } = require("zod");
12
+
13
+ const router = express.Router();
14
+
15
+ const loginSchema = z
16
+ .object({
17
+ username: z
18
+ .string()
19
+ .min(5, { message: "Username should not be less than 5 characters" })
20
+ .max(15, { message: "Username should not be more than 15 characters" }),
21
+ password: z
22
+ .string()
23
+ .min(8, { message: "Password should not be less than 8 characters" })
24
+ .max(15, { message: "Password should not be more than 15 characters" }),
25
+ })
26
+ .required();
27
+
28
+ const signupSchema = z
29
+ .object({
30
+ username: z
31
+ .string()
32
+ .min(5, { message: "Username should not be less than 5 characters" })
33
+ .max(15, { message: "Username should not be more than 15 characters" }),
34
+ email: z.string().email({ message: "Please enter a valid email address" }),
35
+ password: z
36
+ .string()
37
+ .min(8, { message: "Password should not be less than 8 characters" })
38
+ .max(15, { message: "Password should not be more than 15 characters" }),
39
+ })
40
+ .required();
41
+
42
+ router.post("/signup", async (req, res, next) => {
43
+ try {
44
+ const data = { email: req.body.email, password: req.body.password, username: req.body.username };
45
+
46
+ signupSchema.parse(data);
47
+
48
+ // check if username or email already exists
49
+ if (await User.findOne({ username: data.username }))
50
+ return res.status(409).json({ error: { username: "username already taken" } });
51
+ if (await User.findOne({ email: data.email }))
52
+ return res.status(409).json({ error: { email: "user with this email already exists" } });
53
+
54
+ let userData = {
55
+ email: data.email,
56
+ username: data.username,
57
+ password_hash: await generatePasswordHash(data.password),
58
+ };
59
+ let userDoc = await User.create(userData);
60
+ const authToken = createJSONToken(userDoc.id);
61
+
62
+ const { id, username, email } = userDoc;
63
+ res.setHeader('Host',process.env.HOSTNAME).status(201).cookie("auth-token", authToken, { httpOnly: true, sameSite: "strict" }).json({
64
+ user: { id, username, email },
65
+ token: authToken,
66
+ });
67
+ } catch (err) {
68
+ if (err instanceof ZodError) {
69
+ return res.status(400).json({ message: "Invalid data submitted", description: "Invalid schema" });
70
+ }
71
+ next(err);
72
+ }
73
+ });
74
+
75
+ router.post("/login", async (req, res, next) => {
76
+ try {
77
+ let username = req.body.username,
78
+ password = req.body.password;
79
+
80
+ loginSchema.parse({ username, password });
81
+
82
+ let user;
83
+ user = await User.findOne({ username });
84
+ if (!user)
85
+ return res.status(404).json({
86
+ message: "User does not exist",
87
+ description: "'username' not found in db",
88
+ });
89
+
90
+ const pwIsValid = await isValidPassword(password, user.password_hash);
91
+ if (!pwIsValid) {
92
+ return res.status(401).json({
93
+ message: "Invalid credentials",
94
+ description: "Invalid credentials",
95
+ });
96
+ }
97
+
98
+ const token = createJSONToken(user.id);
99
+ res.cookie("auth-token", token, { httpOnly: true, sameSite: "strict" });
100
+ return res.setHeader('Host',process.env.HOSTNAME)
101
+ .status(200)
102
+ .json({ token, user: { id: user.id, username: user.username, email: user.email } });
103
+ } catch (error) {
104
+ if (error instanceof ZodError) {
105
+ return res.status(401).json({ message: "Invalid Credentials", description: "Invalid schema" });
106
+ }
107
+ next(error);
108
+ }
109
+ });
110
+
111
+ router.delete("/logout", checkAuth, (req, res, next) => {
112
+ try {
113
+ res.setHeader('Host',process.env.HOSTNAME).clearCookie("auth-token", { httpOnly: true, sameSite: "strict" });
114
+ res.status(200).json({});
115
+ } catch (err) {
116
+ next(err);
117
+ }
118
+ });
119
+
120
+ module.exports = router;
backend/routes/public.js ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const router = require("express").Router();
2
+ const { Challenge } = require("../models/challenge");
3
+ const { User } = require("../models/user");
4
+
5
+ // API endpoints that are publically accessible and does not require authentication
6
+
7
+ // get all users
8
+ router.get("/users", async (req, res, next) => {
9
+ try {
10
+ const users = await User.find();
11
+ return res.status(200).json(users);
12
+ } catch (err) {
13
+ next(err);
14
+ }
15
+ });
16
+
17
+ // TO BE TESTED
18
+ // get user details
19
+ router.get("/users/:username", async (req, res, next) => {
20
+ try {
21
+ let username = req.params.username;
22
+ const user = await User.findOne({ username });
23
+ let { id, email, fname, lname, country, location } = user;
24
+ let friends = await user.getFriends();
25
+ let games = await user.getGames();
26
+ return res.status(200).json({
27
+ id,
28
+ username,
29
+ email,
30
+ friends,
31
+ fname,
32
+ lname,
33
+ country,
34
+ location,
35
+ games,
36
+ });
37
+ } catch (err) {
38
+ next(err);
39
+ }
40
+ });
41
+
42
+ // TO BE TESTED
43
+ // get friends of given user
44
+ router.get("/users/:username/friends", async (req, res, next) => {
45
+ try {
46
+ const user = await User.findOne({ username: req.params.username });
47
+ const friends = await user.getFriends();
48
+ return res.json({ friends });
49
+ } catch (err) {
50
+ next(err);
51
+ }
52
+ });
53
+
54
+ // IS IT REQUIRED?
55
+ // TO BE TESTED
56
+ // get current challenges of the user
57
+ router.get("/users/:username/challenges", checkAuth, async (req, res, next) => {
58
+ try {
59
+ let { userId } = req;
60
+ const user = await User.findById(userId);
61
+ let challenges = await Challenge.find({ challenged: user.username });
62
+ if (!challenges) challenges = [];
63
+ console.log("Challenges to", user.username, challenges);
64
+ res.json({ challenges: challenges });
65
+ } catch (err) {
66
+ next(err);
67
+ }
68
+ });
69
+
70
+ // TO BE TESTED
71
+ // get history of games played
72
+ router.get("/users/:username/games", async (req, res, next) => {
73
+ try {
74
+ const user = await User.findOne({ username: req.params.username });
75
+ let gamesData = await user.getGames();
76
+ if (!gamesData) gamesData = [];
77
+ return res.status(200).json(gamesData);
78
+ } catch (err) {
79
+ next(err);
80
+ }
81
+ });
82
+
83
+ // TO BE TESTED
84
+ // get a particular game
85
+ router.get(
86
+ "/users/:username/games/:gameid",
87
+ async (req, res, next) => {
88
+ try {
89
+ const { gameid } = req.params;
90
+ const gameData = await Game.findById(gameid);
91
+ if (gameData) {
92
+ return res.status(200).json(gameData);
93
+ } else {
94
+ return res.status(404).json({ error: { message: "Game not found" } });
95
+ }
96
+ } catch (err) {
97
+ next(err);
98
+ }
99
+ }
100
+ );
101
+
102
+ module.exports = router;
backend/routes/room.js ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const router = require("express").Router();
2
+ const uuid = require("uuid");
3
+ const { createRoom } = require("../socket");
4
+ const { checkAuth } = require("../util/auth");
5
+ const { Challenge } = require("../models/challenge");
6
+
7
+ // async function fetchChallenge({ challenger, challenged }) {}
8
+
9
+ // rooms can only be created through HTTP requests and destroyed only by socket.io server
10
+ // and vice versa is not true
11
+ router.post("/", checkAuth, async (req, res, next) => {
12
+ console.log(req.body);
13
+ // challenger and challenged are username, color is the color played by challenger, timeLimit is the timeLimit for one player
14
+ const { challenger, challenged, color, timeLimit } = req.body;
15
+
16
+ let challenge = await Challenge.findOne({ challenger });
17
+ // a user can create only one challenge at a time
18
+ if (challenge) {
19
+ return res.status(405).json({
20
+ message: "Cannot create new challenge",
21
+ description: "User already created a challenge",
22
+ });
23
+ }
24
+
25
+ // get email of the challenged person
26
+ // const challengedEmail = (await User.findOne({ username: challenged })).email;
27
+ // console.log(challengedEmail);
28
+
29
+ const roomID = uuid.v4();
30
+ createRoom(roomID, timeLimit);
31
+
32
+ challenge = await Challenge.create({ challenger, challenged, color, timeLimit, roomID });
33
+
34
+ // if (pendingChallenges.has(challenged)) {
35
+ // let challenges = pendingChallenges.get(challenged);
36
+ // challenges.push({ challenger, roomID, color, timeLimit });
37
+ // } else {
38
+ // // color is the choosed by the challenger
39
+ // pendingChallenges.set(challenged, [{ challenger, roomID, color, timeLimit }]);
40
+ // }
41
+
42
+ console.log("Pending challenge:", challenge);
43
+
44
+ // STOP SENDING EMAILS FOR NOW
45
+ // sendEmail(
46
+ // challengedEmail,
47
+ // `Challenge from ${challenger}`,
48
+ // `To accept the challenge follow the link: http://192.168.136.99:5173/game/challenges/${challenged}/${roomID} \n login through: http://192.168.136.99:5173/login \n roomid:${roomID}`
49
+ // );
50
+ console.log(roomID);
51
+ res.status(201).json({ roomID });
52
+ });
53
+
54
+ module.exports = router;
backend/routes/user.js ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const router = require("express").Router();
2
+ const { Challenge } = require("../models/challenge");
3
+ const { Game } = require("../models/game");
4
+ const { User } = require("../models/user");
5
+ const { checkAuth } = require("../util/auth");
6
+ const { catchAsync } = require("../util/errors");
7
+
8
+ // API enpoints related to a user, requires authentication
9
+
10
+ // extracts the user details of userId. Use only if the req object contains userId field.
11
+ const extractUserDetails = async (req, res, next) => {
12
+ try {
13
+ let { userId } = req;
14
+ if (!userId) throw { message: "userId not found" };
15
+ else {
16
+ let user = await User.findById(userId);
17
+ req.user = user;
18
+ }
19
+ } catch (err) {
20
+ next(err);
21
+ }
22
+ next();
23
+ };
24
+
25
+ // TO BE TESTED
26
+ // get the logged in user details
27
+ router.get("/", checkAuth, extractUserDetails, async (req, res, next) => {
28
+ try {
29
+ let { user } = req;
30
+ let { id, username, email, fname, lname, country, location } = user;
31
+ let friends = await user.getFriends();
32
+ let games = await user.getGames();
33
+ return res.status(200).json({
34
+ id,
35
+ username,
36
+ email,
37
+ friends,
38
+ fname,
39
+ lname,
40
+ country,
41
+ location,
42
+ games,
43
+ });
44
+ } catch (err) {
45
+ next(err);
46
+ }
47
+ });
48
+
49
+ // TO BE TESTED
50
+ // update logged in user details
51
+ router.patch("/", checkAuth, async (req, res, next) => {
52
+ try {
53
+ let { userId } = req;
54
+ let updatedData = req.body;
55
+ await User.findByIdAndUpdate(userId, { ...updatedData });
56
+ let { id, username, email, fname, lname, location, country, fullName } =
57
+ await User.findById(userId);
58
+ return res.status(200).json({
59
+ user: {
60
+ id,
61
+ username,
62
+ email,
63
+ fname,
64
+ lname,
65
+ location,
66
+ country,
67
+ fullName,
68
+ },
69
+ });
70
+ } catch (err) {
71
+ next(err);
72
+ }
73
+ });
74
+
75
+ // TO BE TRIED ONCE
76
+ // TO BE TESTED
77
+ // delete logged in user account
78
+ router.delete("/", checkAuth, extractUserDetails, async (req, res, next) => {
79
+ try {
80
+ let { user } = req;
81
+ await user.deleteOne();
82
+ return res.status(204).json({ message: "Account deleted succesfully" });
83
+ } catch (err) {
84
+ next(err);
85
+ }
86
+ });
87
+
88
+ // TO BE TESTED
89
+ // get all friends of logged in user
90
+ router.get(
91
+ "/friends",
92
+ checkAuth,
93
+ extractUserDetails,
94
+ async (req, res, next) => {
95
+ try {
96
+ let { user } = req;
97
+ let friends = await user.getFriends();
98
+ return res.status(200).json(friends);
99
+ } catch (err) {
100
+ next(err);
101
+ }
102
+ }
103
+ );
104
+
105
+ // TO BE TESTED
106
+ // add a friend
107
+ router.post(
108
+ "/friends",
109
+ checkAuth,
110
+ extractUserDetails,
111
+ async (req, res, next) => {
112
+ let { friendUsername } = req.body;
113
+ const { user } = req;
114
+ if (user.username === friendUsername)
115
+ res.status(405).json({
116
+ error: {
117
+ description: "Cannot add yourself as friend",
118
+ message: "Cannot add this user as friends",
119
+ },
120
+ });
121
+ let friendData = await User.findOne({ username: friendUsername });
122
+ if (friendData) {
123
+ if (friendData.friends.includes(user._id)) {
124
+ res.status(409).json({
125
+ error: {
126
+ message: "User is already added as a friend",
127
+ description: "User is already added as a friend",
128
+ },
129
+ });
130
+ } else {
131
+ friendData.friends.push(user._id);
132
+ await friendData.save();
133
+ user.friends.push(friendData._id);
134
+ await user.save();
135
+ res.status(201).json({});
136
+ }
137
+ } else {
138
+ res.status(404).json({
139
+ error: {
140
+ message: "User not found",
141
+ description: "username not found in DB",
142
+ },
143
+ });
144
+ }
145
+ }
146
+ );
147
+
148
+ // TO BE TESTED
149
+ // remove a user from friends list
150
+ router.delete(
151
+ "/friends",
152
+ checkAuth,
153
+ extractUserDetails,
154
+ catchAsync(async (req, res, next) => {
155
+ const { friendUsername } = req.body;
156
+ const { user } = req;
157
+
158
+ // Find the friend user to be removed
159
+ const friendData = await User.findOne({ username: friendUsername });
160
+ if (!friendData) {
161
+ return res.status(404).json({
162
+ error: {
163
+ message: "Cannot add username that does not exists",
164
+ description: "username to be added as friend not found.",
165
+ },
166
+ });
167
+ }
168
+
169
+ // Remove the friend from the user's friends list
170
+ const friendIndex = user.friends.indexOf(friendData._id);
171
+ if (friendIndex === -1) {
172
+ return res.status(400).json({
173
+ error: { message: "Friend user not found in the friends list" },
174
+ });
175
+ }
176
+ user.friends.splice(friendIndex, 1);
177
+ await user.save();
178
+
179
+ // Remove the user from the friend's friends list
180
+ const userIndex = friendData.friends.indexOf(user._id);
181
+ if (userIndex === -1) {
182
+ return res.status(400).json({
183
+ error: { message: "User not found in the friend's friends list" },
184
+ });
185
+ }
186
+ friendData.friends.splice(userIndex, 1);
187
+ await friendData.save();
188
+
189
+ return res.json({});
190
+ })
191
+ );
192
+
193
+ // TO BE TESTED
194
+ // get all logged in users challenges
195
+ router.get(
196
+ "/challenges",
197
+ checkAuth,
198
+ extractUserDetails,
199
+ async (req, res, next) => {
200
+ try {
201
+ let { user } = req;
202
+ let challenges = await Challenge.find({ challenged: user.username });
203
+ challenges = challenges.map((challenge) => {
204
+ let { id, challenged, challenger, color, roomID, timeLimit } =
205
+ challenge;
206
+ return { id, challenged, challenger, color, roomID, timeLimit };
207
+ });
208
+ console.log(challenges);
209
+ res.status(200).json(challenges);
210
+ } catch (err) {
211
+ next(err);
212
+ }
213
+ }
214
+ );
215
+
216
+ // ??
217
+ // TO BE TESTED
218
+ // TODO: add some logic to notify the challenger if the challenged user declines the challenge
219
+ // accept or decline a challenge
220
+ // challengeID here refers to the roomID associated with the challenge
221
+ router.delete("/challenges/:challengeID", checkAuth, async (req, res, next) => {
222
+ try {
223
+ let { challengeID } = req.params;
224
+ let challenge = await Challenge.findById(challengeID);
225
+ if (!challenge)
226
+ return res.status(404).json({
227
+ message: "Challenge not found",
228
+ description: "Challenge ID does not exists",
229
+ });
230
+ await challenge.deleteOne();
231
+ return res.status(200).json({});
232
+ } catch (err) {
233
+ next(err);
234
+ }
235
+ });
236
+
237
+ // TO BE TESTED
238
+ // get history of games played by logged in user
239
+ router.get("/games", checkAuth, extractUserDetails, async (req, res, next) => {
240
+ try {
241
+ const { user } = req;
242
+ let games = await user.getGames();
243
+ if (!games) games = [];
244
+ return res.status(200).json(games);
245
+ } catch (err) {
246
+ next(err);
247
+ }
248
+ });
249
+
250
+ // TO BE TESTED
251
+ // get game details of a certain game played by logged in user
252
+ router.get(
253
+ "/games/:gameid",
254
+ checkAuth,
255
+ extractUserDetails,
256
+ async (req, res, next) => {
257
+ try {
258
+ let { gameid } = req.params;
259
+ let { user } = req;
260
+ let gameDoc = await Game.findById(gameid);
261
+
262
+ if (!gameDoc) {
263
+ return res.status(404).json({
264
+ message: "Game not found",
265
+ description: "Game id is invalid",
266
+ });
267
+ }
268
+
269
+ if (
270
+ user.id == gameDoc.white._id.toString() ||
271
+ user.id == gameDoc.black._id.toString()
272
+ ) {
273
+ return res.status(200).json(gameDoc);
274
+ } else {
275
+ res.status(404).json({
276
+ message: "Game not found",
277
+ description: "Game id does not belong to the logged in user",
278
+ });
279
+ }
280
+ } catch (err) {
281
+ next(err);
282
+ }
283
+ }
284
+ );
285
+
286
+ // TODO
287
+ // add a game
288
+ router.post("/games", checkAuth, async (req, res, next) => {
289
+ const gameData = req.body;
290
+ const gameDoc = await Game.create(gameData);
291
+ return res.json({ data: gameDoc });
292
+ });
293
+
294
+ module.exports = router;
backend/socket.js ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const socket = require("socket.io");
2
+ const { SOCKET_EVENTS } = require("./constants");
3
+ const { Chess } = require("chess.js");
4
+ const { User } = require("./models/user");
5
+ const { Game } = require("./models/game");
6
+ const { nextMove } = require("./chessbot");
7
+ const {
8
+ CHESS_MOVE,
9
+ CHESS_OPPONENT_MOVE,
10
+ JOIN_ROOM,
11
+ JOIN_ROOM_ERROR,
12
+ JOIN_ROOM_SUCCESS,
13
+ ROOM_FULL,
14
+ USER_JOINED_ROOM,
15
+ USER_RESIGNED,
16
+ GAME_END,
17
+ } = SOCKET_EVENTS;
18
+ // roomID => { timeLimit,gameHistory , players:{'b': {username,userid}, 'w':{username,userid} } }
19
+ let activeRooms = new Map();
20
+
21
+ // chess - chess object
22
+ // moveData - {from,to} => lan notation
23
+ function isValidMove(chess,moveData) {
24
+ let newChess = new Chess();
25
+ newChess.loadPgn(chess.pgn());
26
+ try {
27
+ newChess.move(moveData);
28
+ return true;
29
+ } catch(err) {
30
+ return false;
31
+ }
32
+ }
33
+
34
+ function createRoom(roomID, timeLimit) {
35
+ console.log(roomID, "created");
36
+ activeRooms.set(roomID, { timeLimit, players: {}, gameHistory: [] });
37
+ console.log("Currently active rooms", activeRooms.size);
38
+ }
39
+
40
+ function getRoom(roomID) {
41
+ return activeRooms.get(roomID);
42
+ }
43
+
44
+ function destoryRoom(roomID) {
45
+ activeRooms.delete(roomID);
46
+ }
47
+
48
+ // structure of userDetails: {username,userid,color}
49
+ function addUserToRoom(roomID, socket, userDetails) {
50
+ console.log(userDetails);
51
+ let { username, color, userid } = userDetails;
52
+ let room = activeRooms.get(roomID);
53
+
54
+ if (room.players[color]) {
55
+ room.players[color] = { username, userid, socket };
56
+ return JOIN_ROOM_SUCCESS;
57
+ }
58
+ if (Object.keys(room.players).length > 1) {
59
+ // room is full
60
+ console.log(activeRooms);
61
+ return ROOM_FULL;
62
+ } else {
63
+ room.players[color] = { username, userid, socket };
64
+ }
65
+ console.log(activeRooms);
66
+
67
+ return JOIN_ROOM_SUCCESS;
68
+ }
69
+
70
+ // initialize the socket server with the given http server instance
71
+ function socketIOServerInit(server) {
72
+ const io = new socket.Server(server, {
73
+ cors: {
74
+ origin: process.env.CORS_ALLOWED_HOST,
75
+ },
76
+ });
77
+
78
+ const ioBot = io.of("/chessbot");
79
+
80
+ ioBot.on("connection", (socket) => {
81
+ let id = socket.id;
82
+ console.log(id, "connected on /chessbot");
83
+ const chess = new Chess();
84
+
85
+ socket.onAny((evt) => {
86
+ console.log(evt);
87
+ });
88
+
89
+ socket.on('INIT', async (data) => {
90
+ if(data.color === 'b') {
91
+ console.log(data.color);
92
+ const botMove = await nextMove({position:chess.fen()});
93
+ console.log({ from: botMove.substring(0, 2), to: botMove.substring(2) });
94
+ chess.move({from:botMove.substring(0,2),to:botMove.substring(2)});
95
+ setTimeout(() => {
96
+ socket.emit("CHESS_BOT_MOVE",{from:botMove.substring(0,2),to:botMove.substring(2)})
97
+ }, 500);
98
+ }
99
+ });
100
+
101
+ socket.on("disconnect", (reason) => {
102
+ console.log(id, "disconnected due to", reason);
103
+ });
104
+
105
+ socket.on(CHESS_MOVE, async (roomID, moveData) => {
106
+ console.log("CHESS_MOVE");
107
+ if(!isValidMove(chess,moveData)) return;
108
+ chess.move(moveData);
109
+ let move = moveData.from + moveData.to;
110
+ console.log(move);
111
+ const botMove = await nextMove({ position: chess.fen() });
112
+ console.log({ from: botMove.substring(0, 2), to: botMove.substring(2) });
113
+ chess.move({ from: botMove.substring(0, 2), to: botMove.substring(2) });
114
+ socket.emit("CHESS_BOT_MOVE", { from: botMove.substring(0, 2), to: botMove.substring(2) });
115
+ });
116
+ });
117
+
118
+ io.on("connection", (socket) => {
119
+ let id = socket.id;
120
+ console.log(socket.id, "connected");
121
+
122
+ socket.on("disconnect", (reason) => {
123
+ console.log(id, "disconnected due to", reason);
124
+ });
125
+
126
+ // data is the metadata of the user joining the room played between the users
127
+ // structure: {username,color}
128
+ socket.on(JOIN_ROOM, (roomID, data) => {
129
+ if (activeRooms.has(roomID)) {
130
+ let result = addUserToRoom(roomID, socket, data);
131
+ if (result === JOIN_ROOM_SUCCESS) {
132
+ socket.join(roomID);
133
+ // io.to(roomID).emit("new user joined the room");
134
+ console.log(data, "joined");
135
+ let room = getRoom(roomID);
136
+ socket.to(roomID).emit(USER_JOINED_ROOM, data.username);
137
+ socket.emit(result, room.gameHistory); // room joined successfully
138
+ } else {
139
+ socket.emit(result); // room is full
140
+ }
141
+ } else {
142
+ socket.emit(JOIN_ROOM_ERROR, "room does not exist");
143
+ }
144
+ });
145
+
146
+ socket.on(CHESS_MOVE, (roomID, moveData) => {
147
+ console.log(moveData);
148
+ let room = activeRooms.get(roomID);
149
+ room.gameHistory.push(moveData);
150
+ socket.to(roomID).emit(CHESS_OPPONENT_MOVE, moveData);
151
+ });
152
+
153
+ socket.on(USER_RESIGNED, async (roomID, username) => {
154
+ socket.to(roomID).emit(USER_RESIGNED, username);
155
+ });
156
+
157
+ socket.on(GAME_END, async (roomID) => {
158
+ console.log("Ending game...");
159
+ const room = getRoom(roomID);
160
+ const black = room.players["b"];
161
+ const white = room.players["w"];
162
+ io.socketsLeave(roomID);
163
+ black.socket.disconnect();
164
+ white.socket.disconnect();
165
+
166
+ // generating pgn
167
+ let moves = room.gameHistory;
168
+ let chess = new Chess();
169
+ for (let move of moves) {
170
+ let { from, to } = move;
171
+ chess.move({ from, to });
172
+ }
173
+ let pgn = chess.pgn();
174
+
175
+ const blackPlayerDoc = await User.findById(black.userid);
176
+ const whitePlayerDoc = await User.findById(white.userid);
177
+
178
+ let gameData = {
179
+ white: whitePlayerDoc.id,
180
+ black: blackPlayerDoc.id,
181
+ timeLimit: room.timeLimit,
182
+ roomID,
183
+ pgn,
184
+ };
185
+ const gameDoc = await Game.create(gameData);
186
+
187
+ blackPlayerDoc.games.push(gameDoc.id);
188
+ await blackPlayerDoc.save();
189
+ whitePlayerDoc.games.push(gameDoc.id);
190
+ await whitePlayerDoc.save();
191
+ });
192
+ });
193
+ }
194
+
195
+ module.exports = {
196
+ activeRooms,
197
+ createRoom,
198
+ addUserToRoom,
199
+ socketIOServerInit,
200
+ };
backend/util/auth.js ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const { sign, verify } = require("jsonwebtoken");
2
+ const { compare, hash, genSalt } = require("bcryptjs");
3
+ const { NotAuthError } = require("./errors");
4
+
5
+ const KEY = "supersecret";
6
+
7
+ async function generatePasswordHash(password) {
8
+ const password_hash = await hash(password, await genSalt(10));
9
+ return password_hash;
10
+ }
11
+
12
+ function createJSONToken(id) {
13
+ return sign({ id }, KEY, { noTimestamp: true });
14
+ }
15
+
16
+ function validateJSONToken(token) {
17
+ return verify(token, KEY);
18
+ }
19
+
20
+ function isValidPassword(password, storedPassword) {
21
+ return compare(password, storedPassword);
22
+ }
23
+
24
+ // function checkAuthMiddleware(req, res, next) {
25
+ // if (req.method === "OPTIONS") {
26
+ // return next();
27
+ // }
28
+ // if (!req.headers.authorization) {
29
+ // console.log("NOT AUTH. AUTH HEADER MISSING.");
30
+ // return next(new NotAuthError("Not authenticated."));
31
+ // }
32
+ // const authFragments = req.headers.authorization.split(" ");
33
+
34
+ // if (authFragments.length !== 2) {
35
+ // console.log("NOT AUTH. AUTH HEADER INVALID.");
36
+ // return next(new NotAuthError("Not authenticated."));
37
+ // }
38
+ // const authToken = authFragments[1];
39
+ // try {
40
+ // const validatedToken = validateJSONToken(authToken);
41
+ // req.userid = validatedToken;
42
+ // } catch (error) {
43
+ // console.log("NOT AUTH. TOKEN INVALID.");
44
+ // return next(new NotAuthError("Not authenticated."));
45
+ // }
46
+ // next();
47
+ // }
48
+
49
+ function checkAuthMiddleware(req, res, next) {
50
+ if (req.method === "OPTIONS") {
51
+ return next();
52
+ }
53
+ let authToken = req.cookies["auth-token"];
54
+ if (!authToken) {
55
+ return res.status(401).json({ message: "Not authenticated", description: "Auth token not found" });
56
+ }
57
+ try {
58
+ const validatedToken = validateJSONToken(authToken);
59
+ req.userId = validatedToken.id;
60
+ req.isAuthenticated = true;
61
+ } catch (error) {
62
+ console.log("NOT AUTH. TOKEN INVALID.");
63
+ return res.status(401).json({ message: "Not authenticated", description: "Invalid auth token" });
64
+ }
65
+ next();
66
+ }
67
+
68
+ exports.createJSONToken = createJSONToken;
69
+ exports.validateJSONToken = validateJSONToken;
70
+ exports.isValidPassword = isValidPassword;
71
+ exports.checkAuth = checkAuthMiddleware;
72
+ exports.generatePasswordHash = generatePasswordHash;
backend/util/errors.js ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class NotFoundError {
2
+ constructor(message) {
3
+ this.message = message;
4
+ this.status = 404;
5
+ }
6
+ }
7
+
8
+ class NotAuthError {
9
+ constructor(message) {
10
+ this.message = message;
11
+ this.status = 401;
12
+ }
13
+ }
14
+
15
+ function catchAsync(callback) {
16
+ return (req, res, next) => {
17
+ callback(req, res, next).catch(next);
18
+ };
19
+ }
20
+
21
+ exports.NotFoundError = NotFoundError;
22
+ exports.NotAuthError = NotAuthError;
23
+ exports.catchAsync = catchAsync
backend/util/user.js ADDED
File without changes
backend/util/validation.js ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function isValidText(value, minLength = 1) {
2
+ return value && value.trim().length >= minLength;
3
+ }
4
+
5
+ function isValidDate(value) {
6
+ const date = new Date(value);
7
+ return value && date !== 'Invalid Date';
8
+ }
9
+
10
+ function isValidImageUrl(value) {
11
+ return value && value.startsWith('http');
12
+ }
13
+
14
+ function isValidEmail(value) {
15
+ return value && value.includes('@');
16
+ }
17
+
18
+ exports.isValidText = isValidText;
19
+ exports.isValidDate = isValidDate;
20
+ exports.isValidImageUrl = isValidImageUrl;
21
+ exports.isValidEmail = isValidEmail;
docker-compose-prod.yml ADDED
File without changes
docker-compose.yml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: "3.7"
2
+
3
+ services:
4
+ frontend:
5
+ build: ./frontend
6
+ command: sh -c "npm rebuild esbuild && npm run dev"
7
+ volumes:
8
+ - ./frontend:/usr/src/app
9
+ ports:
10
+ - 127.0.0.1:5173:5173
11
+ backend:
12
+ build: ./backend
13
+ command: nodemon -L app.js
14
+ volumes:
15
+ - ./backend:/usr/src/app
16
+ ports:
17
+ - 127.0.0.1:8080:8080
18
+ environment:
19
+ - PORT=8080
20
+ - CHESS_ENGINE_PATH=engine/stockfish16
frontend/.dockerignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ **/node_modules
2
+ node_modules/*
frontend/.env.example ADDED
@@ -0,0 +1 @@
 
 
1
+ VITE_BACKEND_HOST='http://localhost:8080'
frontend/.eslintignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ node_modules/
2
+ dist/
3
+ .prettierrc.js
4
+ .eslintrc.js
5
+ env.d.ts
frontend/.eslintrc.cjs ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = {
2
+ extends: [
3
+ // By extending from a plugin config, we can get recommended rules without having to add them manually.
4
+ 'eslint:recommended',
5
+ 'plugin:react/recommended',
6
+ 'plugin:import/recommended',
7
+ 'plugin:jsx-a11y/recommended',
8
+ 'plugin:@typescript-eslint/recommended',
9
+ // This disables the formatting rules in ESLint that Prettier is going to be responsible for handling.
10
+ // Make sure it's always the last config, so it gets the chance to override other configs.
11
+ 'eslint-config-prettier',
12
+ ],
13
+ settings: {
14
+ react: {
15
+ // Tells eslint-plugin-react to automatically detect the version of React to use.
16
+ version: 'detect',
17
+ },
18
+ // Tells eslint how to resolve imports
19
+ 'import/resolver': {
20
+ node: {
21
+ paths: ['src'],
22
+ extensions: ['.js', '.jsx', '.ts', '.tsx'],
23
+ },
24
+ },
25
+ },
26
+ rules: {
27
+ // Add your own rules here to override ones from the extended configs.
28
+ "react/react-in-jsx-scope": "off",
29
+ "react/jsx-uses-react": "off",
30
+ "no-console": "off",
31
+ },
32
+ "env":{
33
+ "browser":true,
34
+ "node":true
35
+ }
36
+ };
37
+
frontend/.gitignore ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
25
+
26
+ .env
27
+ test.jsx
frontend/dockerfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dockerfile for React client
2
+
3
+ # Build react client
4
+ FROM node
5
+
6
+ # Working directory be app
7
+ WORKDIR /usr/src/app
8
+
9
+ COPY package*.json ./
10
+
11
+ ### Installing dependencies
12
+
13
+ RUN npm install --silent
14
+
15
+ # copy local files to app folder
16
+ COPY . .
17
+
18
+ EXPOSE 5173
19
+
20
+ CMD ["npm","run","dev"]
frontend/index.html ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/png" href="/favicon.ico" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>ChessHub</title>
8
+ <style>
9
+ body {
10
+ margin: 0;
11
+ width: 100%;
12
+ min-width: 420px;
13
+ }
14
+ </style>
15
+ </head>
16
+ <body>
17
+ <div id="root"></div>
18
+ <div
19
+ id="main-loader"
20
+ ></div>
21
+ <script type="module" src="/src/main.tsx"></script>
22
+ </body>
23
+ </html>
frontend/package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
frontend/package.json ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "chess-",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite --host",
8
+ "build": "vite build",
9
+ "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10
+ "preview": "vite preview",
11
+ "test": "vitest"
12
+ },
13
+ "dependencies": {
14
+ "@dnd-kit/core": "^6.0.8",
15
+ "@emotion/react": "^11.11.1",
16
+ "@mantine/core": "^6.0.14",
17
+ "@mantine/form": "^6.0.16",
18
+ "@mantine/hooks": "^6.0.14",
19
+ "@radix-ui/react-icons": "^1.3.0",
20
+ "@tabler/icons-react": "^2.23.0",
21
+ "@typescript-eslint/eslint-plugin": "^6.2.0",
22
+ "@typescript-eslint/parser": "^6.2.0",
23
+ "chess.js": "^1.0.0-beta.6",
24
+ "eslint": "^8.45.0",
25
+ "eslint-config-prettier": "^8.8.0",
26
+ "eslint-plugin-import": "^2.27.5",
27
+ "eslint-plugin-jsx-a11y": "^6.7.1",
28
+ "eslint-plugin-react": "^7.33.0",
29
+ "framer-motion": "^11.2.3",
30
+ "js-cookie": "^3.0.5",
31
+ "prettier": "^3.0.0",
32
+ "react": "^18.2.0",
33
+ "react-dom": "^18.2.0",
34
+ "react-hook-form": "^7.45.2",
35
+ "react-router-dom": "^6.14.0",
36
+ "socket.io-client": "^4.7.1",
37
+ "zod": "^3.21.4"
38
+ },
39
+ "devDependencies": {
40
+ "@testing-library/jest-dom": "^5.17.0",
41
+ "@testing-library/react": "^14.0.0",
42
+ "@testing-library/user-event": "^14.4.3",
43
+ "@types/react": "^18.3.4",
44
+ "@types/react-dom": "^18.3.0",
45
+ "@vitejs/plugin-react": "^4.0.0",
46
+ "eslint-plugin-react-hooks": "^4.6.0",
47
+ "eslint-plugin-react-refresh": "^0.3.4",
48
+ "jest": "^29.6.1",
49
+ "jest-environment-jsdom": "^29.6.1",
50
+ "typescript": "^5.5.4",
51
+ "vite": "^4.3.9",
52
+ "vitest": "^0.33.0"
53
+ }
54
+ }
frontend/public/assets/audio/capture.mp3 ADDED
Binary file (6.89 kB). View file
 
frontend/public/assets/audio/castle.mp3 ADDED
Binary file (5.35 kB). View file
 
frontend/public/assets/audio/game-end.webm ADDED
Binary file (5.22 kB). View file
 
frontend/public/assets/audio/move-check.mp3 ADDED
Binary file (6.12 kB). View file
 
frontend/public/assets/audio/move-self.mp3 ADDED
Binary file (3.43 kB). View file
 
frontend/public/assets/audio/notify.mp3 ADDED
Binary file (3.82 kB). View file
 
frontend/public/assets/audio/promote.mp3 ADDED
Binary file (4.97 kB). View file
 
frontend/public/assets/images/bishop_black.png ADDED
frontend/public/assets/images/bishop_white.png ADDED
frontend/public/assets/images/chess_board.png ADDED
frontend/public/assets/images/chess_board_loader.png ADDED
frontend/public/assets/images/king_black.png ADDED
frontend/public/assets/images/king_white.png ADDED