From e4e9284f11cf5ca26dc2c7a779e17e39fc412610 Mon Sep 17 00:00:00 2001 From: FabienBounoir Date: Wed, 30 Oct 2024 19:34:04 +0100 Subject: [PATCH 01/18] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20init=20external=20?= =?UTF-8?q?backend=20with=20socket=20io?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 1 + backend/index.js | 84 +++ backend/package-lock.json | 1019 ++++++++++++++++++++++++++ backend/package.json | 17 + src/lib/rooms.ts => backend/rooms.js | 14 +- ws.js => backend/server.js | 232 +++--- package-lock.json | 88 ++- package.json | 2 + src/lib/webSocketConnection.js | 5 + src/routes/+layout.svelte | 2 + src/routes/create/+page.svelte | 45 +- src/routes/join/+page.svelte | 25 +- src/routes/manager/[id]/+page.svelte | 101 +-- src/routes/rooms/[id]/+page.svelte | 144 ++-- vite.config.ts | 57 +- 15 files changed, 1505 insertions(+), 331 deletions(-) create mode 100644 backend/index.js create mode 100644 backend/package-lock.json create mode 100644 backend/package.json rename src/lib/rooms.ts => backend/rooms.js (68%) rename ws.js => backend/server.js (58%) create mode 100644 src/lib/webSocketConnection.js diff --git a/.env.example b/.env.example index e69de29..de7b3b0 100644 --- a/.env.example +++ b/.env.example @@ -0,0 +1 @@ +VITE_BACKEND_URL="http://localhost:5876" \ No newline at end of file diff --git a/backend/index.js b/backend/index.js new file mode 100644 index 0000000..f70d01f --- /dev/null +++ b/backend/index.js @@ -0,0 +1,84 @@ +const express = require('express'); +const http = require('http'); +const { createRoomId, rooms } = require('./rooms'); +const { createSocketIOServer } = require('./server'); +const cors = require('cors') + +// Créez une instance Express pour les routes API +const app = express(); + +// Middleware pour interpréter le JSON dans les requêtes +app.use(express.json()); + +app.use(cors()) + +app.get("/api/room", (req, res) => { + console.log("req.query;", req.query) + const { roomId } = req.query; + + if (rooms.get(roomId)) { + return res.json({ roomId }); + } + + return res.status(400).json({ error: "Room doesn't exist." }); +}) + +// Route API pour créer une nouvelle salle +app.post('/api/room', (req, res) => { + const { type, team } = req.body; + + if (!["TSHIRT", "FIBONACCI", "POWEROF2", "SEQUENTIAL", "TSHIRT_HALF"].includes(type)) { + return res.status(400).json({ error: "Invalid room type" }); + } + + const roomId = createRoomId(); + const formattedTeam = (team || 'NFS').trim().charAt(0).toUpperCase() + (team || 'NFS').slice(1).toLowerCase(); + + // Initialisation des données de la salle + const roomData = { + team: formattedTeam, + cards: [], + state: 'waiting', + userStory: '' + }; + + // Associer les cartes en fonction du type de salle + switch (type) { + case "TSHIRT": + roomData.cards = ['XS', 'S', 'M', 'L', 'XL', 'XXL']; + break; + case "FIBONACCI": + roomData.cards = ["1", "2", "3", "5", "8", "13", "21"]; + break; + case "POWEROF2": + roomData.cards = ["1", "2", "4", "8", "16", "32"]; + break; + case "SEQUENTIAL": + roomData.cards = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]; + break; + case "TSHIRT_HALF": + roomData.cards = ['XS', 'S', 'M', 'M/L', 'L', 'XL']; + break; + default: + return res.status(400).json({ error: "Unknown room type" }); + } + + // Ajouter la nouvelle salle à la liste des salles + rooms.set(roomId, { initialisation: true, data: roomData }); + console.log("NEW ROOM CREATED", rooms); + + // Répondre avec l'ID de la salle + res.json({ roomId }); +}); + +// Création du serveur HTTP et intégration d'Express +const server = http.createServer(app); + +// Créez le serveur Socket.IO en l'associant au serveur HTTP +createSocketIOServer(server, rooms); + +// Démarrez le serveur +const PORT = 5876; +server.listen(PORT, () => { + console.log(`Server listening on port ${PORT}`); +}); diff --git a/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 0000000..d71cd10 --- /dev/null +++ b/backend/package-lock.json @@ -0,0 +1,1019 @@ +{ + "name": "backend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "backend", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "cors": "^2.8.5", + "express": "^4.21.1", + "socket.io": "^4.8.1", + "uuid": "^11.0.2" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.8.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.4.tgz", + "integrity": "sha512-SpNNxkftTJOPk0oN+y2bIqurEXHTA2AOZ3EJDDKeJ5VzkvvORSvmQXGQarcOzWV1ac7DCaPBEdMDxBsM+d8jWw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz", + "integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..57b99ca --- /dev/null +++ b/backend/package.json @@ -0,0 +1,17 @@ +{ + "name": "backend", + "version": "1.0.0", + "description": "Websocket for poker planning", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "cors": "^2.8.5", + "express": "^4.21.1", + "socket.io": "^4.8.1", + "uuid": "^11.0.2" + } +} diff --git a/src/lib/rooms.ts b/backend/rooms.js similarity index 68% rename from src/lib/rooms.ts rename to backend/rooms.js index e1ae981..b7e3c17 100644 --- a/src/lib/rooms.ts +++ b/backend/rooms.js @@ -1,8 +1,6 @@ -import { writable } from "svelte/store"; +const rooms = new Map() -export const rooms = new Map() - -export const createRoomId = () => { +const createRoomId = () => { let newId; do { const part1 = Math.floor(Math.random() * 1000).toString().padStart(3, '0'); @@ -13,6 +11,12 @@ export const createRoomId = () => { return newId; }; -export const roomExist = (roomId) => { +const roomExist = (roomId) => { return rooms.has(roomId) } + +module.exports = { + rooms, + roomExist, + createRoomId +} \ No newline at end of file diff --git a/ws.js b/backend/server.js similarity index 58% rename from ws.js rename to backend/server.js index 9c92d44..a59a809 100644 --- a/ws.js +++ b/backend/server.js @@ -1,23 +1,20 @@ -import { WebSocket, WebSocketServer } from 'ws'; -import { v4 as uuidv4 } from 'uuid'; -import { createRoomId } from './src/lib/rooms'; +const { Server } = require('socket.io'); +const { v4: uuidv4 } = require('uuid'); +const { createRoomId } = require('./rooms'); /** - * @type {WebSocketServer} + * @type {Server} */ -let wss; +let io; -export const createWSSGlobalInstance = (rooms) => { - if (wss) return wss; +const createSocketIOServer = (server, rooms) => { + if (io) return io; - /** - * @param {string} id - */ function getRooms(roomId) { - let room = rooms.get(roomId) + let room = rooms.get(roomId); if (!room) { - return null + return null; } if (room?.initialisation) { @@ -32,16 +29,14 @@ export const createWSSGlobalInstance = (rooms) => { object.players.forEach((player) => { if (manager) { if (player.manager) { - player.socket.send(JSON.stringify({ type, data })); + player.socket.emit(type, data); } - } - else { - player.socket.send(JSON.stringify({ type, data })); + } else { + player.socket.emit(type, data); } }); }, emitPlayers() { - /** @type {any[]} */ const players = []; object.players.forEach((player, id) => { if (!player.manager) { @@ -50,71 +45,64 @@ export const createWSSGlobalInstance = (rooms) => { }); object.players.forEach((player) => { - // if (player.manager) { - player.socket.send(JSON.stringify({ type: "players", data: players })); - // } + player.socket.emit("players", players); }); }, emitUpdateGame(state) { - let element = { ...object.data } - + let element = { ...object.data }; if (state == "result") { - let results = new Map() - let itemsName = [] + let results = new Map(); + let itemsName = []; object.players.forEach((player, id) => { if (!player.manager && player.selectedCard) { - let item = results.get(player?.selectedCard?.toUpperCase?.()) + let item = results.get(player?.selectedCard?.toUpperCase?.()); if (!item) { - itemsName.push(player?.selectedCard?.toUpperCase?.()) - item = [] + itemsName.push(player?.selectedCard?.toUpperCase?.()); + item = []; } - item.push(player.name) - - results.set(player?.selectedCard?.toUpperCase?.(), item) + item.push(player.name); + results.set(player?.selectedCard?.toUpperCase?.(), item); } }); if (itemsName.length > 0) { try { if (itemsName.length > 1) { - const item = itemsName[Math.floor(Math.random() * itemsName.length)] - const itemUsers = results.get(item) - const userSelected = itemUsers[Math.floor(Math.random() * itemUsers.length)] + const item = itemsName[Math.floor(Math.random() * itemsName.length)]; + const itemUsers = results.get(item); + const userSelected = itemUsers[Math.floor(Math.random() * itemUsers.length)]; object.data.defender = element.defender = { name: userSelected, - item - } + item, + }; } - let resultArray = Array.from(results).sort((a, b) => b[1].length - a[1].length) - - object.data.result = element.result = resultArray - } - catch (e) { - console.error("ERROR WHEN PROCESS RESULT", e) + let resultArray = Array.from(results).sort((a, b) => b[1].length - a[1].length); + object.data.result = element.result = resultArray; + } catch (e) { + console.error("ERROR WHEN PROCESS RESULT", e); } } - } - else { - object.data.result = object.data.defender = null + } else { + object.data.result = object.data.defender = null; } object.emit('game-update', element); }, resetChoose() { object.players.forEach((player) => { - object.players.set(player.socket.userId, { ...player, selectedCard: null }) - }) - this.emitPlayers() - } + object.players.set(player.socket.userId, { ...player, selectedCard: null }); + }); + this.emitPlayers(); + }, }; if (object.data) { - object.data = { ...object.data, ...room.data } + object.data = { ...object.data, ...room.data }; } rooms.set(roomId, (room = object)); @@ -128,55 +116,48 @@ export const createWSSGlobalInstance = (rooms) => { return trimmedName.charAt(0).toUpperCase() + trimmedName.slice(1).toLowerCase(); } + io = new Server(server, { cors: { origin: "*" } }); - wss = new WebSocketServer({ noServer: true }); - wss.on( - 'connection', - ( - /** @type {WebSocket} */ ws, - /** @type {string} */ roomId, - /** @type {string} */ name, - /** @type {boolean} */ manager, - ) => { - const room = getRooms(roomId) - console.log("> Room", room) + io.on('connection', (socket) => { + socket.on('join', ({ roomId, name, manager }) => { + const room = getRooms(roomId); + + console.log(roomId, name, manager) if (!room) { - console.warn("Want to join room doesn't exist") - return ws.close(1000, "Room doesn't exist"); + console.warn("Room doesn't exist"); + return socket.emit("error", { reason: "Room doesn't exist" }); } if (!name) { - console.warn("User without name") - return ws.close(1000, "Can't join without name"); + console.warn("User without name"); + return socket.disconnect(true); } const userId = uuidv4(); console.log('New connection with ID:', userId); - //check if room as timeout setup if (room.timeout) { clearTimeout(room.timeout); delete room.timeout; console.log("Timeout removed for room", roomId, "because user", userId, "reconnected."); } - ws.userId = userId; - //send to user actual state of game - ws.send(JSON.stringify({ type: "game-update", data: room.data })); + socket.userId = userId; + socket.emit("game-update", room.data); const player = { - socket: ws, + socket, name: formatName(name), selectedCard: null, - manager + manager, }; room.players.set(userId, player); room.emitPlayers(); - ws.on('close', () => { - console.log(`User ${userId} disconnected`) + socket.on("leave-room", () => { + console.log(`User leave room ${userId}`); room.players.delete(userId); if (room.players.size) { @@ -185,115 +166,114 @@ export const createWSSGlobalInstance = (rooms) => { room.timeout = setTimeout(() => { if (!room.players.size) { rooms.delete(roomId); - console.log(`Room ${roomId} supprimée après inactivité.`); + console.log(`Room ${roomId} deleted after inactivity.`); } }, 3600000); } - }); + }) + socket.on('disconnect', () => { + console.log(`User ${userId} disconnected`); + room.players.delete(userId); - ws.on('message', async (raw) => { - const { type, data } = JSON.parse(raw.toString()); + if (room.players.size) { + room.emitPlayers(); + } else { + room.timeout = setTimeout(() => { + if (!room.players.size) { + rooms.delete(roomId); + console.log(`Room ${roomId} deleted after inactivity.`); + } + }, 3600000); + } + }); + socket.on('message', ({ type, data }) => { switch (type) { - //Game event case 'vote': { const player = room.players.get(userId); - if (!room?.data?.cards?.includes?.(data.card)) { - return - } - - player.selectedCard = data.card - room.players.set(userId, player) - room.emitPlayers() - ws.send(JSON.stringify({ type: "success", success: true })); + if (!room?.data?.cards?.includes?.(data.card)) return; - } + player.selectedCard = data.card; + room.players.set(userId, player); + room.emitPlayers(); + socket.emit("success", { success: true }); break; + } case 'state': { - room.data.state = data.state + room.data.state = data.state; if (data.userStory) { - room.data.userStory = data.userStory + room.data.userStory = data.userStory; } - if (data.state == "playing") { - room.resetChoose() + if (data.state === "playing") { + room.resetChoose(); } - room.emitUpdateGame(data.state) - } + room.emitUpdateGame(data.state); break; + } case 'ping': - ws.send(JSON.stringify({ type: "pong", success: true })); + socket.emit("pong", { success: true }); break; - - //Fun Event case 'hexcode': - room.data.hexcode = data.hexcode - room.emit("hexcode", { hexcode: data.hexcode }, false) + room.data.hexcode = data.hexcode; + room.emit("hexcode", { hexcode: data.hexcode }, false); break; - - default: { - console.log(`EVENT ${type} doesn't exist`) - } + default: + console.log(`EVENT ${type} doesn't exist`); } }); - } - ); - - wss.on( - 'create', - ( - /** @type {WebSocket} */ ws, - /** @type {string} */ type, - /** @type {string} */ team, - ) => { - if (type && !["TSHIRT", "FIBONACCI", "POWEROF2", "SEQUENTIAL", "TSHIRT_HALF"].includes(type)) { - console.warn(`Error: want to create room with type: ${type}`) - return ws.close(1000, "Type doesn't exist"); + }); + + socket.on('create', ({ type, team }) => { + if (!["TSHIRT", "FIBONACCI", "POWEROF2", "SEQUENTIAL", "TSHIRT_HALF"].includes(type)) { + console.warn(`Invalid room type: ${type}`); + return socket.disconnect(true); } - const roomId = createRoomId() + const roomId = createRoomId(); rooms.set(roomId, { reserved: true }); let object = { team: formatName(team || 'NFS'), cards: [], state: 'waiting', - userStory: '' - } + userStory: '', + }; switch (type) { case "TSHIRT": object.cards = ['XS', 'S', 'M', 'L', 'XL', 'XXL']; break; - case "FIBONACCI": object.cards = ["1", "2", "3", "5", "8", "13", "21"]; break; - case "POWEROF2": object.cards = ["1", "2", "4", "8", "16", "32"]; break; - case "SEQUENTIAL": object.cards = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]; break; - case "TSHIRT_HALF": object.cards = ['XS', 'S', 'M', 'M/L', 'L', 'XL']; break; - default: console.warn(`Unknown type: ${type}`); break; } rooms.set(roomId, { initialisation: true, data: object }); - console.log("NEW ROOM CREATE", rooms) + console.log("NEW ROOM CREATED", rooms); - ws.send(JSON.stringify({ type: "created", data: { roomId } })); - }) + socket.emit("created", { roomId }); + }); + }); - return wss; + return io; }; + + +module.exports = { + createSocketIOServer +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 640573b..5adef0c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "chroma-js": "^3.1.2", "preprocess": "^3.2.0", + "socket.io-client": "^4.8.1", "svelte-confetti": "^2.1.2", "svelte-sonner": "^0.3.28", "uuid": "^10.0.0", @@ -1227,6 +1228,12 @@ "win32" ] }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, "node_modules/@sveltejs/adapter-auto": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.3.0.tgz", @@ -1811,7 +1818,6 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -1862,6 +1868,49 @@ "dev": true, "license": "MIT" }, + "node_modules/engine.io-client": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.2.tgz", + "integrity": "sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", @@ -2662,7 +2711,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -3184,6 +3232,34 @@ "node": ">=18" } }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -3623,6 +3699,14 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/xregexp": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-3.1.0.tgz", diff --git a/package.json b/package.json index 0139e60..260a245 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.0.1", "type": "module", "scripts": { + "startserv": "node server.js", "dev": "vite dev", "build": "vite build", "preview": "vite preview", @@ -32,6 +33,7 @@ "dependencies": { "chroma-js": "^3.1.2", "preprocess": "^3.2.0", + "socket.io-client": "^4.8.1", "svelte-confetti": "^2.1.2", "svelte-sonner": "^0.3.28", "uuid": "^10.0.0", diff --git a/src/lib/webSocketConnection.js b/src/lib/webSocketConnection.js new file mode 100644 index 0000000..89a3592 --- /dev/null +++ b/src/lib/webSocketConnection.js @@ -0,0 +1,5 @@ +import ioClient from 'socket.io-client'; + +const socket = ioClient(import.meta.env.VITE_BACKEND_URL); + +export const io = socket; \ No newline at end of file diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 35162a7..1883c6b 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,6 +1,8 @@ diff --git a/src/routes/create/+page.svelte b/src/routes/create/+page.svelte index 8a5e664..6383c9a 100644 --- a/src/routes/create/+page.svelte +++ b/src/routes/create/+page.svelte @@ -3,33 +3,32 @@ import { onDestroy, onMount } from 'svelte'; import { quintOut } from 'svelte/easing'; import { scale } from 'svelte/transition'; + import { io } from '$lib/webSocketConnection.js'; let type = $state(); let team = $state(''); let submitting = $state(false); - let ws; - const create = async () => { submitting = true; try { - const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - ws = new WebSocket( - `${protocol}//${window.location.host}/create-room?${new URLSearchParams({ + const myHeaders = new Headers(); + myHeaders.append('Content-Type', 'application/json'); + + const res = await fetch(`${import.meta.env.VITE_BACKEND_URL}/api/room`, { + method: 'POST', + headers: myHeaders, + body: JSON.stringify({ type, team - })}` - ); - - ws.onmessage = (e) => { - const payload = JSON.parse(e.data); - console.log('Event Payload', payload); - switch (payload.type) { - case 'created': - goto(`/manager/${payload?.data.roomId}`); - break; - } - }; + }) + }) + .then((response) => response.json()) + .catch((error) => console.error(error)); + + if (res && res.roomId) { + goto(`/manager/${res.roomId}`); + } } catch (error) { console.log('Create Error', error); } @@ -50,16 +49,6 @@ team = window.localStorage.getItem('team') || ''; }); - onDestroy(() => { - try { - if (ws) { - ws.close(); - } - } catch (error) { - console.log('Destroy Error', error); - } - }); - $effect(() => { window.localStorage.setItem('type', type); }); @@ -71,7 +60,7 @@

Créer un nouveau poker planning 🃏

-
+ { + on:click={() => { goto('/create'); }}>Pas de code ? diff --git a/src/routes/manager/[id]/+page.svelte b/src/routes/manager/[id]/+page.svelte index 7c8e0ed..994fe00 100644 --- a/src/routes/manager/[id]/+page.svelte +++ b/src/routes/manager/[id]/+page.svelte @@ -39,7 +39,8 @@ }; const connect = () => { - const io = ioClient(import.meta.env.VITE_BACKEND_URL); + io = ioClient(import.meta.env.VITE_BACKEND_URL); + io.connect(); io.emit('join', { roomId, name: 'ADMIN', manager: true }); // 858-616 @@ -92,8 +93,6 @@ }; const checkSocketConnected = () => { - console.log('io.connected', io.connected); - if (!io.connected) { toast.error('Websocket not connected...'); return false; @@ -128,12 +127,12 @@
{#if pokerManager.state == 'playing'} {:else if pokerManager.state == 'result' || pokerManager.state == 'waiting'} - + {/if}
diff --git a/src/routes/rooms/[id]/+page.svelte b/src/routes/rooms/[id]/+page.svelte index db51f04..a4e23c4 100644 --- a/src/routes/rooms/[id]/+page.svelte +++ b/src/routes/rooms/[id]/+page.svelte @@ -66,7 +66,7 @@ try { submitting = true; - const io = ioClient(import.meta.env.VITE_BACKEND_URL); + io = ioClient(import.meta.env.VITE_BACKEND_URL); io.emit('join', { roomId, name: username }); @@ -198,14 +198,14 @@

Bienvenue 👋
Comment tu t'appelles déjà ?

- +
{:else}
-
+
@@ -252,7 +252,7 @@ +{#if displayQrCode} +
(displayQrCode = false)} transition:fade> + +

{displayText}

+
+{/if} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 73c4479..59ca87e 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -11,7 +11,7 @@ .beta { position: fixed; top: 0; - right: 0; + left: 0; margin: 1em 1.5em; border-radius: 9999px; background: linear-gradient(120deg, var(--primary-200), var(--primary-500), var(--primary-800)); diff --git a/src/routes/manager/[id]/+page.svelte b/src/routes/manager/[id]/+page.svelte index a91cba9..ea048aa 100644 --- a/src/routes/manager/[id]/+page.svelte +++ b/src/routes/manager/[id]/+page.svelte @@ -10,12 +10,16 @@ import { backOut } from 'svelte/easing'; import { fly } from 'svelte/transition'; import ioClient from 'socket.io-client'; + import Confetti from 'svelte-confetti'; let roomId = $page.params.id; let url = $state(''); let io: Socket; + let resultsItem = $state(null); + let resultDefender = $state(null); + let pokerManager = $state({ team: '', cards: ['XS', 'S', 'M', 'L', 'XL', 'XXL'], @@ -23,7 +27,7 @@ userStory: '' }); - let users = $state([]); + let users = $state(null); const canStarVote = () => { if (pokerManager.userStory == '') { @@ -65,6 +69,22 @@ primary: payload.hexcode }); } + resultsItem = null; + resultDefender = null; + + if (payload?.state == 'result') { + if (payload?.result) { + resultsItem = payload.result; + } + + if (payload?.defender) { + resultDefender = payload.defender; + } + + console.log('resultsItem', resultsItem); + console.log('resultDefender', resultDefender); + } + pokerManager = payload; }); @@ -73,13 +93,6 @@ primary: payload.hexcode }); }); - - io.on('reconnect', (attempt) => { - console.info(`Reconnecté après ${attempt} tentatives.`); - toast.success('Reconnecté au serveur !'); - - io.emit('join', { roomId, name: 'ADMIN' }); - }); }; onMount(() => { @@ -112,6 +125,33 @@ Poker Planning: {pokerManager.team} +{#if resultsItem} + {#if resultsItem?.length == 1 && resultsItem?.[0]?.[1]?.length > 1} +
+ +
+ {/if} +{/if} +
@@ -146,29 +186,44 @@
- {#if users?.length < 1} -
-

Pas de participants pour la planification du poker.

-
- {:else} -

{users.length} player{users.length > 1 ? 's' : ''}

- {/if} + {#if users != null} + {#if users?.length < 1} +
+

Pas de participants pour la planification du poker.

+
+ {:else} +

{users.length} player{users.length > 1 ? 's' : ''}

+ {/if} - {#each users as user} -
-
- -

{user.name}

+ {#each users as user} +
+
+ +

{user.name}

+
+ {#if pokerManager.state === 'playing'} +

+ {:else if pokerManager.state === 'result'} +

{user.selectedCard}

+ {:else} +

-

+ {/if} +
+ {/each} + {:else} + {#each Array(Math.floor(Math.random() * 5) + 3).fill(0) as _} +
+
+
+ +
- {#if pokerManager.state === 'playing'} -

- {:else if pokerManager.state === 'result'} -

{user.selectedCard}

- {:else} -

-

- {/if} -
- {/each} + {/each} + {/if}
@@ -246,6 +301,11 @@ background-color: var(--primary-200); font-weight: 600; color: var(--primary-950); + box-sizing: border-box; + + &.defender { + border: 3px solid var(--primary-800); + } .profile { display: flex; @@ -258,6 +318,38 @@ width: 40px; height: 40px; } + + .img-skeleton { + border: 2px solid var(--primary-700); + width: 40px; + height: 40px; + background-color: var(--primary-500); + border-radius: 100%; + } + + .name-skeleton { + background-color: var(--primary-500); + width: 100px; + height: 20px; + border-radius: 5px; + background: linear-gradient( + 120deg, + var(--primary-300), + var(--primary-500), + var(--primary-700) + ); + background-size: 200% 200%; + animation: shimmer 1s infinite alternate; + + @keyframes shimmer { + 0% { + background-position: 0% 50%; + } + 100% { + background-position: 100% 50%; + } + } + } } .no-vote { diff --git a/src/routes/rooms/[id]/+page.svelte b/src/routes/rooms/[id]/+page.svelte index bb3177b..ceb7478 100644 --- a/src/routes/rooms/[id]/+page.svelte +++ b/src/routes/rooms/[id]/+page.svelte @@ -75,7 +75,7 @@ }); io.on('game-update', (payload) => { - console.log('payload', payload); + console.log('Payload', payload); resultsItem = null; resultDefender = null; @@ -101,7 +101,7 @@ }); io.on('success', (payload) => { - console.log('payload sucess', payload); + console.log('Payload success', payload); if (payload?.success) { if (timeout) { clearTimeout(timeout); @@ -128,16 +128,6 @@ goto('/join'); } }); - - io.on('reconnect', (attempt) => { - console.info(`Reconnecté après ${attempt} tentatives.`); - toast.success('Reconnecté au serveur !'); - - io.emit('join', { roomId, name: username }); - if (submittedLetter != null) { - sendVote(submittedLetter); - } - }); } catch (e) { console.error('Websocket error', e); } finally { @@ -302,8 +292,8 @@ y={[0, 0.1]} delay={[500, 2000]} infinite - duration="5000" - amount="200" + duration={5000} + amount={200} fallDistance="100vh" />