diff --git a/.env b/.env new file mode 100644 index 0000000..2b9961f --- /dev/null +++ b/.env @@ -0,0 +1 @@ +BASE_URL=https://code-lab-backend-one.vercel.app/ \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..a817a2a --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,6 @@ +module.exports = { + root: true, + extends: [ + 'next/core-web-vitals', + ], +} diff --git a/Backend/.gitignore b/.gitignore similarity index 97% rename from Backend/.gitignore rename to .gitignore index a547bf3..41f028b 100644 --- a/Backend/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ node_modules dist dist-ssr *.local +.next/ # Editor directories and files .vscode/* diff --git a/Backend/.env b/Backend/.env deleted file mode 100644 index 22fdb16..0000000 --- a/Backend/.env +++ /dev/null @@ -1,2 +0,0 @@ -PORT=8000 -SECERT_KEY=wertyuikjhgfdrtyuioolkjhb \ No newline at end of file diff --git a/Backend/README.md b/Backend/README.md deleted file mode 100644 index 445c8c1..0000000 --- a/Backend/README.md +++ /dev/null @@ -1 +0,0 @@ -# CodeLab \ No newline at end of file diff --git a/Backend/index.js b/Backend/index.js deleted file mode 100644 index a5d9268..0000000 --- a/Backend/index.js +++ /dev/null @@ -1,35 +0,0 @@ -const express = require("express"); -const http = require("http"); -const cors = require("cors"); -const dotenv = require("dotenv"); -const cookieParser = require("cookie-parser"); - -const authRoute = require("./routes/auth"); -const setupSocketIO = require("./socket/socket"); -const protectedRoutes = require("./routes/protectedRoutes"); - -dotenv.config(); -const PORT = process.env.PORT; - -const corsOptions = { - origin: "http://localhost:5173", - methods: ["POST", "PUT", "GET", "OPTIONS", "HEAD"], - credentials: true, -}; - -const app = express(); - -app.use(cookieParser()); -app.use(cors(corsOptions)); -app.options("*", cors(corsOptions)); -app.use(express.json()); -app.use(express.urlencoded({ extended: true })); -app.use("/user", authRoute); -app.use("/protected", protectedRoutes); - -const server = http.createServer(app); -setupSocketIO(server); - -server.listen(PORT, () => { - console.log(`Server started at http://localhost:${PORT}`); -}); diff --git a/Backend/middleware/authMiddleware.js b/Backend/middleware/authMiddleware.js deleted file mode 100644 index 905c952..0000000 --- a/Backend/middleware/authMiddleware.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Middleware to verify JWT token. - * - * This middleware function is used to verify the JWT token provided in the request header. - * It checks if the token exists and is valid. If the token is valid, it decodes the token, - * attaches the decoded user information to the request object, and calls the next middleware - * in the stack. If the token is missing or invalid, it sends an appropriate error response. - * - * @param {Object} req - The request object. - * @param {Object} res - The response object. - * @param {Function} next - The next middleware function in the stack. - * - * @returns {void} - If the token is valid, it calls the next middleware. - * @returns {Object} 401 - If the token is missing or invalid, it sends an error response. - */ -const jwt = require("jsonwebtoken"); - -const verifyToken = (req, res, next) => { - try { - // Extract the token from the AuthorizationToken header - const token = req.cookies.AuthToken; - console.log("meddleware token: ", token) - - // If no token is provided, send a 401 Unauthorized response - if (!token) { - return res.status(401).json({ error: "Access denied" }); - } - - // Verify the token and decode it - const decode = jwt.verify(token, process.env.SECERT_KEY); - - // Attach the decoded user information to the request object - req.userId = decode.userId; - req.username = decode.username; - req.email = decode.email; - - // Proceed to the next middleware - next(); - } catch (err) { - // If the token is invalid, send a 401 Unauthorized response - return res.status(401).json({ error: "Invalid token" }); - } -}; - -module.exports = verifyToken; diff --git a/Backend/models/user.js b/Backend/models/user.js deleted file mode 100644 index 441648b..0000000 --- a/Backend/models/user.js +++ /dev/null @@ -1,22 +0,0 @@ -const mongoose = require("mongoose"); - -mongoose.connect("mongodb://localhost:27017/test"); - -const userSchema = new mongoose.Schema({ - username: { - type: String, - required: true, - }, - email: { - type: String, - required: true, - unique: true, - }, - password: { - type: String, - required: true, - }, - rooms: [], -}); - -module.exports = mongoose.model("users", userSchema); diff --git a/Backend/package-lock.json b/Backend/package-lock.json deleted file mode 100644 index aa1654a..0000000 --- a/Backend/package-lock.json +++ /dev/null @@ -1,1840 +0,0 @@ -{ - "name": "backend", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "backend", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "bcrypt": "^5.1.1", - "cookie-parser": "^1.4.6", - "cors": "^2.8.5", - "dotenv": "^16.4.5", - "express": "^4.19.2", - "jsonwebtoken": "^9.0.2", - "mongoose": "^8.2.4", - "socket.io": "^4.7.5" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@mongodb-js/saslprep": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.5.tgz", - "integrity": "sha512-XLNOMH66KhJzUJNwT/qlMnS4WsNDWD5ASdyaSH3EtK+F4r/CFGa3jT4GNi4mfOitGvWXtdLgQJkQjxSVrio+jA==", - "dependencies": { - "sparse-bitfield": "^3.0.3" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" - }, - "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==" - }, - "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==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "20.12.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.2.tgz", - "integrity": "sha512-zQ0NYO87hyN6Xrclcqp7f8ZbXNbRfoGWNcMvHTPQp9UUrwI0mI7XBz+cu7/W6/VClYo2g63B0cjull/srU7LgQ==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/webidl-conversions": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" - }, - "node_modules/@types/whatwg-url": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.4.tgz", - "integrity": "sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw==", - "dependencies": { - "@types/webidl-conversions": "*" - } - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "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==", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "node_modules/bcrypt": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", - "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", - "hasInstallScript": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.11", - "node-addon-api": "^5.0.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "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.11.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/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/bson": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.5.0.tgz", - "integrity": "sha512-DXf1BTAS8vKyR90BO4x5v3rKVarmkdkzwOrnYDFdjAY694ILNDkmA3uRh1xXJEl+C1DAh8XCvAQ+Gh3kzubtpg==", - "engines": { - "node": ">=16.20.1" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "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==", - "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/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "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==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", - "dependencies": { - "cookie": "0.4.1", - "cookie-signature": "1.0.6" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/cookie-parser/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "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==" - }, - "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==", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "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==", - "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/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "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==", - "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==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "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==" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "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==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/engine.io": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", - "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", - "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.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.11.0" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", - "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "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==", - "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==", - "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==" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "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/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "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/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "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==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "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==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "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/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "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==", - "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==", - "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==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "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==", - "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/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "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==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "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==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jsonwebtoken/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==" - }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/kareem": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", - "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "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==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "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==", - "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==", - "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==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mongodb": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", - "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", - "dependencies": { - "@mongodb-js/saslprep": "^1.1.0", - "bson": "^6.2.0", - "mongodb-connection-string-url": "^3.0.0" - }, - "engines": { - "node": ">=16.20.1" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", - "socks": "^2.7.1" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "gcp-metadata": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - }, - "socks": { - "optional": true - } - } - }, - "node_modules/mongodb-connection-string-url": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", - "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", - "dependencies": { - "@types/whatwg-url": "^11.0.2", - "whatwg-url": "^13.0.0" - } - }, - "node_modules/mongoose": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.4.tgz", - "integrity": "sha512-da/r6zpG+2eAXuhBGUnL6jcBd03zlytoCc5/wq+LyTsmrY9hhPQmSpnugwnfqldtBmUOhB6iMLoV4hNtHRq+ww==", - "dependencies": { - "bson": "^6.2.0", - "kareem": "2.5.1", - "mongodb": "6.3.0", - "mpath": "0.9.0", - "mquery": "5.0.0", - "ms": "2.1.3", - "sift": "16.0.1" - }, - "engines": { - "node": ">=16.20.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mongoose" - } - }, - "node_modules/mongoose/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==" - }, - "node_modules/mpath": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", - "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mquery": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", - "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", - "dependencies": { - "debug": "4.x" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/mquery/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mquery/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "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==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "node_modules/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "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==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "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==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "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==", - "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==", - "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/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "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/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "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==", - "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==" - }, - "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==", - "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/sift": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", - "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/socket.io": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", - "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.5.2", - "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.4", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz", - "integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==", - "dependencies": { - "debug": "~4.3.4", - "ws": "~8.11.0" - } - }, - "node_modules/socket.io-adapter/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-adapter/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "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==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", - "dependencies": { - "memory-pager": "^1.0.2" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "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==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", - "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", - "dependencies": { - "punycode": "^2.3.0" - }, - "engines": { - "node": ">=14" - } - }, - "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==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "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==", - "engines": { - "node": ">= 0.4.0" - } - }, - "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==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", - "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", - "dependencies": { - "tr46": "^4.1.1", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "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/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } -} diff --git a/Backend/package.json b/Backend/package.json deleted file mode 100644 index ae865a8..0000000 --- a/Backend/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "backend", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "bcrypt": "^5.1.1", - "cookie-parser": "^1.4.6", - "cors": "^2.8.5", - "dotenv": "^16.4.5", - "express": "^4.19.2", - "jsonwebtoken": "^9.0.2", - "mongoose": "^8.2.4", - "socket.io": "^4.7.5" - } -} diff --git a/Backend/routes/auth.js b/Backend/routes/auth.js deleted file mode 100644 index 2cd4607..0000000 --- a/Backend/routes/auth.js +++ /dev/null @@ -1,192 +0,0 @@ -const express = require("express"); -const jwt = require("jsonwebtoken"); -const bcrypt = require("bcrypt"); -const router = express.Router(); - -const userModel = require("../models/user"); - -/** - * Validates the complexity of a password. - * - * This function checks if a given password meets the following criteria: - * - At least 8 characters long. - * - Contains at least one uppercase letter. - * - Contains at least one lowercase letter. - * - Contains at least one digit. - * - Contains at least one special character from the set [@$!%*?&]. - * - * @param {string} password - The password to validate. - * @returns {boolean} - Returns `true` if the password meets all the criteria, otherwise `false`. - * - * @example - * const isValid = passwordValidater('Password123!'); - * console.log(isValid); // true - * - * @example - * const isValid = passwordValidater('password'); - * console.log(isValid); // false - */ -const passwordValidater = (password) => { - const regex = - /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/; - return regex.test(password); -}; - -/** - * Register a new user. - * - * This route handles user registration. It validates the request body for required fields - * (username, email, password), checks if the password meets the complexity requirements, - * and ensures that no user already exists with the provided email. If all checks pass, - * it hashes the password, creates a new user in the database. - * - * @route POST /register - * @group Authentication - Operations about user authentication - * @param {string} username.body.required - The username of the new user. - * @param {string} email.body.required - The email of the new user. - * @param {string} password.body.required - The password of the new user. - * @returns {object} 201 - Successfully registered a new user. Returns the user's details and a JWT token. - * @returns {object} 400 - Missing required fields or password does not meet complexity requirements. - * @returns {object} 409 - A user already exists with the given email ID. - * @returns {object} 500 - Internal server error. - */ -router.post("/register", async (req, res) => { - try { - const { username, email, password } = req.body; - - // Check for required fields - if (!username) { - res.status(400).json({ message: "Username Required" }); - return; - } - if (!email) { - res.status(400).json({ message: "Email Required" }); - return; - } - if (!password) { - res.status(400).json({ message: "Password Required" }); - return; - } - - // Validate password complexity - if (!passwordValidater(password)) { - res.status(400).json({ - message: - "Password must contain at least 8 characters, 1 uppercase, 1 lowercase, 1 number, and 1 special character", - }); - return; - } - - // Check if user already exists - let user = await userModel.findOne({ email: email }); - if (user) { - res - .status(409) - .json({ message: "User already exists with given email Id" }); - return; - } - - // Hash password and create new user - const hashedPassword = await bcrypt.hash(password, 10); - const userData = new userModel({ - username: username, - email: email, - password: hashedPassword, - }); - - await userData.save(); - - // Generate and return JWT token - const token = jwt.sign( - { userId: userData._id, username: username, email: email }, - process.env.SECERT_KEY, - { - expiresIn: "24h", - } - ); - - res.cookie("AuthToken", token, { maxAge: 600000000, httpOnly: true }); - - res.status(201).json({ - userId: userData._id, - username: userData.username, - email: userData.email, - }); - } catch (err) { - // Handle any errors that occur during the registration process - res.status(500).json({ - message: "Unable to register user", - error: err, - }); - } -}); - -/** - * Authenticate a user and return a JWT token. - * - * This route handles user authentication. It validates the request body for required fields - * (email and password), checks if the user exists in the database, and verifies the password. - * If the user is authenticated successfully, it returns the user's details. - * - * @route POST /login - * @group Authentication - Operations about user authentication - * @param {string} email.body.required - The email of the user. - * @param {string} password.body.required - The password of the user. - * @returns {object} 400 - Missing required fields (email or password). - * @returns {object} 401 - User not found or authentication failed (wrong password). - * @returns {object} 200 - Successfully authenticated. Returns the user's details and a JWT token. - * @returns {object} 500 - Internal server error. - */ -router.post("/login", async (req, res) => { - try { - const { email, password } = req.body; - - // Check for required fields - if (!email) { - return res.status(400).json({ message: "Email Required" }); - } - if (!password) { - return res.status(400).json({ message: "Password Required" }); - } - - // Find user by email - const user = await userModel.findOne({ email: email }); - if (!user) { - return res.status(401).json({ message: "User Not Found" }); - } - - // Verify password - const encryptedPassword = await bcrypt.compare(password, user.password); - if (!encryptedPassword) { - return res - .status(401) - .json({ message: "Authentication failed: Wrong Password" }); - } - - // Generate and return JWT token - const token = jwt.sign( - { userId: user._id, username: user.username, email: email }, - process.env.SECERT_KEY, - { - expiresIn: "24h", - } - ); - - res.cookie("AuthToken", token, { maxAge: 600000000, httpOnly: true }); - console.log("login token: ", token) - - res.status(200).json({ - userId: user._id, - username: user.username, - email: user.email, - }); - } catch (err) { - // Handle any errors that occur during the authentication process - return res.status(500).json({ - message: "Unable to authenticate user", - error: err, - }); - } -}); - -module.exports = router; diff --git a/Backend/routes/protectedRoutes.js b/Backend/routes/protectedRoutes.js deleted file mode 100644 index 2fd3ac8..0000000 --- a/Backend/routes/protectedRoutes.js +++ /dev/null @@ -1,36 +0,0 @@ -const express = require("express"); -const router = express.Router(); - -const userModel = require("../models/user"); -const verifyToken = require("../middleware/authMiddleware"); - -router.post("/addRoom", verifyToken, async (req, res) => { - try { - const { roomId } = req.body; - - if (!roomId) { - return res.status(400).json({ error: "Room Id is required" }); - } - - const user = await userModel.findById(req.userId); - user.rooms.push(roomId); - await user.save(); - - return res.status(201).json({ message: "Room added successfully" }); - } catch (err) { - console.error(err); - return res.status(500).json({ error: "Internal server error" }); - } -}); - -router.get("/isLoggedIn", verifyToken, (req, res) => { - return res.status(200).json({ login: true, message: "User Logged In" }); -}); - -router.get("/logout", verifyToken, (req, res) => { - res.cookie("AuthToken", "", { maxAge: 0 }); - - return res.status(200).json({ message: "Logout successful" }); -}); - -module.exports = router; diff --git a/Backend/socket/socket.js b/Backend/socket/socket.js deleted file mode 100644 index 68d314b..0000000 --- a/Backend/socket/socket.js +++ /dev/null @@ -1,59 +0,0 @@ -const { Server } = require("socket.io"); - -const setupSocketIO = (server) => { - const roomsAndClientsmap = {}; - - const io = new Server(server, { - cors: { - origin: "*", - methods: ["GET", "POST", "PUT", "DELETE"], - allowedHeaders: ["my-custom-header"], - credentials: true, - }, - }); - - io.on("connection", (socket) => { - socket.on("JOIN", ({ roomId, username }) => { - socket.join(roomId); - if (roomId in roomsAndClientsmap) { - roomsAndClientsmap[roomId].clients.push({ - socketId: socket.id, - username: username || "Unknown Client", - }); - } else { - roomsAndClientsmap[roomId] = { - code: "// your code...", - clients: [ - { - socketId: socket.id, - username: username, - }, - ], - }; - } - - io.to(socket.id).emit("JOINED", { - code: roomsAndClientsmap[roomId].code, - clients: roomsAndClientsmap[roomId].clients, - }); - }); - - socket.on("code change", ({ roomId, newCode }) => { - roomsAndClientsmap[roomId].code = newCode; - socket.in(roomId).emit("code change", { newCode }); - }); - - socket.on("leave", (roomId) => { - const clients = roomsAndClientsmap[roomId].clients; - const index = clients.findIndex((cli) => cli.socketId == socket.id); - const client = clients[index]; - - if (index !== -1) clients.splice(index, 1); - - socket.in(roomId).emit("disconnected", { username: client.username }); - socket.leave(); - }); - }); -}; - -module.exports = setupSocketIO; diff --git a/Frontend/.eslintrc.cjs b/Frontend/.eslintrc.cjs deleted file mode 100644 index 3e212e1..0000000 --- a/Frontend/.eslintrc.cjs +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - 'plugin:react-hooks/recommended', - ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - settings: { react: { version: '18.2' } }, - plugins: ['react-refresh'], - rules: { - 'react/jsx-no-target-blank': 'off', - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, -} diff --git a/Frontend/.gitignore b/Frontend/.gitignore deleted file mode 100644 index a547bf3..0000000 --- a/Frontend/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/Frontend/README.md b/Frontend/README.md deleted file mode 100644 index f768e33..0000000 --- a/Frontend/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# React + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh diff --git a/Frontend/index.html b/Frontend/index.html deleted file mode 100644 index dd8b4c4..0000000 --- a/Frontend/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - CodeLab - - -
- - - diff --git a/Frontend/package.json b/Frontend/package.json deleted file mode 100644 index a4905a2..0000000 --- a/Frontend/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "frontend", - "private": true, - "proxy": "http://localhost:8000", - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" - }, - "dependencies": { - "@monaco-editor/react": "^4.6.0", - "axios": "^1.6.8", - "monaco-editor": "^0.44.0", - "react": "^18.2.0", - "react-cookie": "^7.1.4", - "react-dom": "^18.2.0", - "react-monaco-editor": "^0.55.0", - "react-router-dom": "^6.22.3", - "sass": "^1.72.0", - "scss": "^0.2.4", - "socket.io-client": "^4.7.5", - "uuid": "^9.0.1" - }, - "devDependencies": { - "@types/react": "^18.2.66", - "@types/react-dom": "^18.2.22", - "@vitejs/plugin-react": "^4.2.1", - "eslint": "^8.57.0", - "eslint-plugin-react": "^7.34.1", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.6", - "vite": "^5.2.0" - } -} diff --git a/Frontend/public/vite.svg b/Frontend/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/Frontend/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Frontend/src/App.jsx b/Frontend/src/App.jsx deleted file mode 100644 index 34f105d..0000000 --- a/Frontend/src/App.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { useCallback, useState } from "react"; -import Editor from "@monaco-editor/react"; - -import style from "./App.module.scss"; -import { NavBar } from "./components"; -import { images } from "./constant"; - -function App() { - return ( - <> - - -
-
-
-
-

Unleash the Power of Collaboration⚡️

-

- CodeLab is a collaborative online real-time code editor for - technical -
- interviews, pair programming, teaching... you name it. -

- -
- - New file -
-
-
-
- - ); -} - -export default App; diff --git a/Frontend/src/components/Editor/Editor.jsx b/Frontend/src/components/Editor/Editor.jsx deleted file mode 100644 index d3ea623..0000000 --- a/Frontend/src/components/Editor/Editor.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from "react"; - -import style from "./Editor.module.scss"; -import NavBar from "../NavBar/NavBar"; - -const Editor = () => { - return ( - <> - - - ); -}; - -export default Editor; diff --git a/Frontend/src/components/Editor/Editor.module.scss b/Frontend/src/components/Editor/Editor.module.scss deleted file mode 100644 index e69de29..0000000 diff --git a/Frontend/src/components/ErrorPage/ErrorPage.jsx b/Frontend/src/components/ErrorPage/ErrorPage.jsx deleted file mode 100644 index dee0642..0000000 --- a/Frontend/src/components/ErrorPage/ErrorPage.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; - -import "./ErrorPage.scss"; - -const ErrorPage = () => { - return <>; -}; - -export default ErrorPage; diff --git a/Frontend/src/components/ErrorPage/ErrorPage.scss b/Frontend/src/components/ErrorPage/ErrorPage.scss deleted file mode 100644 index e69de29..0000000 diff --git a/Frontend/src/components/Home/Home.jsx b/Frontend/src/components/Home/Home.jsx deleted file mode 100644 index 0023411..0000000 --- a/Frontend/src/components/Home/Home.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; - -import "./Home.scss" - -const Home = () => { - return <>; -}; - -export default Home; diff --git a/Frontend/src/components/Home/Home.scss b/Frontend/src/components/Home/Home.scss deleted file mode 100644 index e69de29..0000000 diff --git a/Frontend/src/components/NavBar/NavBar.jsx b/Frontend/src/components/NavBar/NavBar.jsx deleted file mode 100644 index 57b0923..0000000 --- a/Frontend/src/components/NavBar/NavBar.jsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { Link } from "react-router-dom"; - -import { images } from "../../constant"; -import "../../style/btn2.scss"; -import style from "./NavBar.module.scss"; -import axios from "axios"; - -const NavBar = () => { - const [isLoggedIn, setLoggedIn] = useState(true); - - useEffect(() => { - axios - .get("http://localhost:8000/protected/isLoggedIn", { - withCredentials: true, - }) - .then((res) => { - if (res.response.status == 200) { - setLoggedIn(true); - } - }) - .catch((err) => { - if (err.response.status == 401) { - setLoggedIn(true); - } - - console.log(err); - }); - }, []); - - return ( - <> -
-
- - - -
-
-
- - New file -
-
- {isLoggedIn ? ( - -
-
- - - - Dashboard -
-
- - ) : ( -
- - - Sign in - - -
- )} -
-
-
- - ); -}; - -export default NavBar; diff --git a/Frontend/src/components/NavBar/NavBar.module.scss b/Frontend/src/components/NavBar/NavBar.module.scss deleted file mode 100644 index c8fc55c..0000000 --- a/Frontend/src/components/NavBar/NavBar.module.scss +++ /dev/null @@ -1,98 +0,0 @@ -.nav{ - width: 100%; - background-color: #fefaf3; - - padding: 10px 20px; - position: fixed; - - .nav__menu{ - height: 50px; - width: calc(100% - 40px); - - display: flex; - justify-content: space-between; - align-items: center; - - img{ - width: 120px; - } - } - - .nav__list{ - width: 20%; - display: flex; - justify-content: space-around; - align-items: center; - - .new_file{ - div{ - width: 120px; - padding: 5px 10px 5px 0px; - position: relative; - will-change: transform; - transform: translateY(0); - cursor: pointer; - font-size: 16px; - font-weight: 600; - transition: transform 150ms, box-shadow 150ms; - - svg{ - padding: 5px 10px; - width: 15px; - } - - &:hover{ - transform: translateY(-2px); - } - - &:active{ - box-shadow: none; - transform: translateY(0); - } - } - } - - .dashboard{ - color: white; - border-radius: 10px; - background-color: #5f5fff; - - div{ - width: 120px; - padding: 5px 10px 5px 0px; - position: relative; - cursor: pointer; - font-size: 16px; - font-weight: 600; - - svg{ - padding: 5px; - width: 24px; - } - } - - &:active{ - box-shadow: none; - transform: scale(0.95) - } - } - - .signin{ - - a{ - text-decoration: none; - color: black; - - span{ - font-size: 17px; - cursor: pointer; - - img{ - padding: 5px 10px; - width: 20px; - } - } - } - } - } -} \ No newline at end of file diff --git a/Frontend/src/components/index.js b/Frontend/src/components/index.js deleted file mode 100644 index 7006910..0000000 --- a/Frontend/src/components/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import Home from "./Home/Home"; -import NavBar from "./NavBar/NavBar"; -import ErrorPage from "./ErrorPage/ErrorPage"; -import SignUp from "./SignUp/SignUp"; -import Login from "./Login/Login"; -import Editor from "./Editor/Editor"; - -export { Home, NavBar, ErrorPage, SignUp, Login, Editor }; diff --git a/Frontend/src/index.css b/Frontend/src/index.css deleted file mode 100644 index 13727d4..0000000 --- a/Frontend/src/index.css +++ /dev/null @@ -1,27 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=Darker+Grotesque:wght@300..900&family=Outfit:wght@100..900&display=swap'); - - -:root { - font-family: "Outfit", sans-serif; - --primary-color: #f8f7fa; - --white-color: #ffffff; - --blue-color: #7367f0; - --dark-blue-color: #675cd8; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -* { - margin: 0; - padding: 0; - box-shadow: none; -} - -.app__flex{ - display: flex; - justify-content: center; - align-items: center; -} \ No newline at end of file diff --git a/Frontend/src/main.jsx b/Frontend/src/main.jsx deleted file mode 100644 index 9ad80ed..0000000 --- a/Frontend/src/main.jsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import { v4 as uuidV4 } from "uuid"; -import { createBrowserRouter, RouterProvider } from "react-router-dom"; - -import { Editor, ErrorPage, Login, SignUp } from "./components"; -import App from "./App.jsx"; -import "./index.css"; - -const router = new createBrowserRouter([ - { - path: "/", - element: , - errorElement: , - }, - { - path: "/register", - element: , - errorElement: , - }, - { - path: "/login", - element: , - }, - { - path: "/editor", - element:
-
- }, - { - path: "/editor/:roomId", - element: , - }, -]); - -ReactDOM.createRoot(document.getElementById("root")).render( - - - -); diff --git a/Frontend/vite.config.js b/Frontend/vite.config.js deleted file mode 100644 index 5a33944..0000000 --- a/Frontend/vite.config.js +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], -}) diff --git a/README.md b/README.md index 445c8c1..313d1b5 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# CodeLab \ No newline at end of file +# CodeLab (In Development phase) \ No newline at end of file diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..2a2e4b3 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "paths": { + "@/*": ["./*"] + } + } +} diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..40c3d68 --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. diff --git a/next.config.js b/next.config.js new file mode 100644 index 0000000..4678774 --- /dev/null +++ b/next.config.js @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/Frontend/package-lock.json b/package-lock.json similarity index 99% rename from Frontend/package-lock.json rename to package-lock.json index 3bef014..13bb483 100644 --- a/Frontend/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "react": "^18.2.0", "react-cookie": "^7.1.4", "react-dom": "^18.2.0", + "react-hot-toast": "^2.4.1", "react-monaco-editor": "^0.55.0", "react-router-dom": "^6.22.3", "sass": "^1.72.0", @@ -2708,6 +2709,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/goober": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz", + "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -3841,6 +3850,21 @@ "react": "^18.2.0" } }, + "node_modules/react-hot-toast": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz", + "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==", + "dependencies": { + "goober": "^2.1.10" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/package.json b/package.json new file mode 100644 index 0000000..13d740b --- /dev/null +++ b/package.json @@ -0,0 +1,37 @@ +{ + "name": "codelab", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "node server.js", + "build": "next build", + "start": "NODE_ENV=production node server.js", + "lint": "next lint" + }, + "dependencies": { + "@monaco-editor/react": "^4.6.0", + "axios": "^1.6.8", + "bcryptjs": "^2.4.3", + "dotenv": "^17.4.2", + "jsonwebtoken": "^9.0.3", + "monaco-editor": "^0.44.0", + "mongoose": "^8.24.0", + "next": "^14.2.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-hot-toast": "^2.4.1", + "sass": "^1.72.0", + "socket.io": "^4.8.3", + "socket.io-client": "^4.7.5", + "uuid": "^9.0.1" + }, + "devDependencies": { + "@types/node": "^25.9.3", + "@types/react": "^19.2.17", + "@types/uuid": "^10.0.0", + "eslint": "^8.57.0", + "eslint-config-next": "^14.2.0", + "typescript": "^6.0.3" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..9a0816e --- /dev/null +++ b/server.js @@ -0,0 +1,24 @@ +import { createServer } from "http"; +import next from "next"; +import { config } from "dotenv"; +import { setupSocketIO } from "./src/lib/socket.js"; + +config(); + +const dev = process.env.NODE_ENV !== "production"; +const app = next({ dev }); +const handle = app.getRequestHandler(); + +const port = process.env.PORT || 3000; + +app.prepare().then(() => { + const server = createServer((req, res) => { + handle(req, res); + }); + + setupSocketIO(server); + + server.listen(port, () => { + console.log(`> Ready on http://localhost:${port}`); + }); +}); diff --git a/Frontend/src/App.module.scss b/src/App.module.scss similarity index 100% rename from Frontend/src/App.module.scss rename to src/App.module.scss diff --git a/src/app/api/projects/[id]/duplicate/route.ts b/src/app/api/projects/[id]/duplicate/route.ts new file mode 100644 index 0000000..9dd3444 --- /dev/null +++ b/src/app/api/projects/[id]/duplicate/route.ts @@ -0,0 +1,70 @@ +import { NextRequest, NextResponse } from "next/server"; +import dbConnect from "@/src/lib/db"; +import Project from "@/src/lib/models/project"; +import { verifyToken } from "@/src/lib/middleware/auth"; +import type { AuthSuccess } from "@/src/lib/middleware/auth"; + +function isAuthSuccess( + auth: ReturnType +): auth is AuthSuccess { + return "userId" in auth; +} + +export async function POST( + request: NextRequest, + { params }: { params: { id: string } } +) { + const authResult = verifyToken(request); + if (!isAuthSuccess(authResult)) { + return NextResponse.json({ error: authResult.error }, { status: authResult.status }); + } + const auth = authResult; + + try { + await dbConnect(); + + const original = await Project.findById(params.id); + if (!original) { + return NextResponse.json( + { success: false, error: "Project not found" }, + { status: 404 } + ); + } + + const userId = auth.userId; + const isOwner = original.owner.toString() === userId; + const isCollaborator = original.collaborators?.some( + (c) => c.userId.toString() === userId + ); + + if (!isOwner && !isCollaborator) { + return NextResponse.json( + { success: false, error: "Access denied" }, + { status: 403 } + ); + } + + const duplicate = await Project.create({ + title: `${original.title} (Copy)`, + description: original.description, + programmingLanguage: original.programmingLanguage, + visibility: "private", + owner: auth.userId, + collaborators: [ + { userId: auth.userId, role: "owner", joinedAt: new Date() }, + ], + code: original.code || "// your code...", + }); + + return NextResponse.json( + { success: true, data: duplicate }, + { status: 201 } + ); + } catch (err) { + console.error("Error duplicating project:", err); + return NextResponse.json( + { success: false, error: "Failed to duplicate project" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/projects/[id]/route.ts b/src/app/api/projects/[id]/route.ts new file mode 100644 index 0000000..d4d1d60 --- /dev/null +++ b/src/app/api/projects/[id]/route.ts @@ -0,0 +1,166 @@ +import { NextRequest, NextResponse } from "next/server"; +import dbConnect from "@/src/lib/db"; +import Project from "@/src/lib/models/project"; +import { verifyToken } from "@/src/lib/middleware/auth"; +import type { AuthSuccess } from "@/src/lib/middleware/auth"; + +function isAuthSuccess( + auth: ReturnType +): auth is AuthSuccess { + return "userId" in auth; +} + +export async function GET( + request: NextRequest, + { params }: { params: { id: string } } +) { + const authResult = verifyToken(request); + const isAuthenticated = isAuthSuccess(authResult); + const userId = isAuthenticated ? (authResult as AuthSuccess).userId : null; + + try { + await dbConnect(); + + const project = await Project.findById(params.id) + .populate("owner", "username email avatar") + .populate("collaborators.userId", "username email avatar") + .lean(); + + if (!project) { + return NextResponse.json( + { success: false, error: "Project not found" }, + { status: 404 } + ); + } + + const hasOwner = !!project.owner; + const isOwner = hasOwner && userId && String(project.owner) === userId; + const isCollaborator = + userId && + project.collaborators?.some((c) => String(c.userId) === userId); + + if (hasOwner && !isOwner && !isCollaborator && project.visibility !== "public") { + return NextResponse.json( + { success: false, error: "Access denied" }, + { status: 403 } + ); + } + + return NextResponse.json({ success: true, data: project }, { status: 200 }); + } catch (err) { + console.error("Error fetching project:", err); + return NextResponse.json( + { success: false, error: "Failed to fetch project" }, + { status: 500 } + ); + } +} + +export async function PUT( + request: NextRequest, + { params }: { params: { id: string } } +) { + const authResult = verifyToken(request); + const isAuthenticated = isAuthSuccess(authResult); + const userId = isAuthenticated ? (authResult as AuthSuccess).userId : null; + + try { + await dbConnect(); + + const project = await Project.findById(params.id); + if (!project) { + return NextResponse.json( + { success: false, error: "Project not found" }, + { status: 404 } + ); + } + + const hasOwner = !!project.owner; + const isOwner = hasOwner && userId && project.owner.toString() === userId; + const isEditor = userId && + project.collaborators?.some( + (c) => c.userId.toString() === userId && c.role === "editor" + ); + + if (hasOwner && !isOwner && !isEditor) { + return NextResponse.json( + { success: false, error: "Access denied" }, + { status: 403 } + ); + } + + const body = await request.json(); + const allowedFields = [ + "title", + "description", + "programmingLanguage", + "visibility", + "code", + "archived", + ]; + + for (const field of allowedFields) { + if (body[field] !== undefined) { + if (field === "title" && !body.title?.trim()) { + return NextResponse.json( + { success: false, error: "Title cannot be empty" }, + { status: 400 } + ); + } + (project as unknown as Record)[field] = body[field]; + } + } + + await project.save(); + + return NextResponse.json({ success: true, data: project }, { status: 200 }); + } catch (err) { + console.error("Error updating project:", err); + return NextResponse.json( + { success: false, error: "Failed to update project" }, + { status: 500 } + ); + } +} + +export async function DELETE( + request: NextRequest, + { params }: { params: { id: string } } +) { + const authResult = verifyToken(request); + const isAuthenticated = isAuthSuccess(authResult); + const userId = isAuthenticated ? (authResult as AuthSuccess).userId : null; + + try { + await dbConnect(); + + const project = await Project.findById(params.id); + if (!project) { + return NextResponse.json( + { success: false, error: "Project not found" }, + { status: 404 } + ); + } + + const hasOwner = !!project.owner; + if (hasOwner && (!userId || project.owner.toString() !== userId)) { + return NextResponse.json( + { success: false, error: "Only the owner can delete a project" }, + { status: 403 } + ); + } + + await Project.findByIdAndDelete(params.id); + + return NextResponse.json( + { success: true, message: "Project deleted" }, + { status: 200 } + ); + } catch (err) { + console.error("Error deleting project:", err); + return NextResponse.json( + { success: false, error: "Failed to delete project" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/projects/route.ts b/src/app/api/projects/route.ts new file mode 100644 index 0000000..daf740f --- /dev/null +++ b/src/app/api/projects/route.ts @@ -0,0 +1,102 @@ +import { NextRequest, NextResponse } from "next/server"; +import dbConnect from "@/src/lib/db"; +import Project from "@/src/lib/models/project"; +import { verifyToken } from "@/src/lib/middleware/auth"; +import type { AuthSuccess } from "@/src/lib/middleware/auth"; + +function isAuthSuccess( + auth: ReturnType +): auth is AuthSuccess { + return "userId" in auth; +} + +export async function GET(request: NextRequest) { + const authResult = verifyToken(request); + if (!isAuthSuccess(authResult)) { + return NextResponse.json({ error: authResult.error }, { status: authResult.status }); + } + const auth = authResult; + + try { + await dbConnect(); + + const { searchParams } = new URL(request.url); + const search = searchParams.get("search") || ""; + const archived = searchParams.get("archived"); + + const query: Record = { + $or: [ + { owner: auth.userId }, + { "collaborators.userId": auth.userId }, + ], + }; + + if (archived === "true") query.archived = true; + else if (archived !== "all") query.archived = false; + + if (search) { + query.$text = { $search: search }; + } + + const projects = await Project.find(query) + .populate("owner", "username email avatar") + .populate("collaborators.userId", "username email avatar") + .sort({ updatedAt: -1 }) + .lean(); + + return NextResponse.json({ success: true, data: projects }, { status: 200 }); + } catch (err) { + console.error("Error fetching projects:", err); + return NextResponse.json( + { success: false, error: "Failed to fetch projects" }, + { status: 500 } + ); + } +} + +export async function POST(request: NextRequest) { + const authResult = verifyToken(request); + const isAuthenticated = isAuthSuccess(authResult); + const auth = isAuthenticated ? (authResult as AuthSuccess) : null; + + try { + await dbConnect(); + + const body = await request.json(); + const { title, description, programmingLanguage, visibility } = body; + + if (!title || !title.trim()) { + return NextResponse.json( + { success: false, error: "Title is required" }, + { status: 400 } + ); + } + + const project = await Project.create({ + title: title.trim(), + description: description?.trim() || "", + programmingLanguage: programmingLanguage || "javascript", + visibility: visibility || "private", + owner: auth?.userId || null, + ...(auth + ? { + collaborators: [ + { userId: auth.userId, role: "owner", joinedAt: new Date() }, + ], + } + : {}), + code: "// your code...", + }); + + return NextResponse.json( + { success: true, data: project }, + { status: 201 } + ); + } catch (err) { + console.error("Error creating project:", err); + return NextResponse.json( + { success: false, error: "Failed to create project" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/protected/addRoom/route.js b/src/app/api/protected/addRoom/route.js new file mode 100644 index 0000000..0b9cdd4 --- /dev/null +++ b/src/app/api/protected/addRoom/route.js @@ -0,0 +1,28 @@ +import { NextResponse } from "next/server"; +import dbConnect from "@/src/lib/db"; +import User from "@/src/lib/models/user"; +import { verifyToken } from "@/src/lib/middleware/auth"; + +export async function POST(request) { + try { + const auth = verifyToken(request); + if (auth.error) { + return NextResponse.json({ error: auth.error }, { status: auth.status }); + } + + await dbConnect(); + + const { roomId } = await request.json(); + if (!roomId) { + return NextResponse.json({ error: "Room Id is required" }, { status: 400 }); + } + + const user = await User.findById(auth.userId); + user.rooms.push(roomId); + await user.save(); + + return NextResponse.json({ message: "Room added successfully" }, { status: 201 }); + } catch (err) { + return NextResponse.json({ error: "Internal server error" }, { status: 500 }); + } +} diff --git a/src/app/api/protected/isLoggedIn/route.js b/src/app/api/protected/isLoggedIn/route.js new file mode 100644 index 0000000..45da78a --- /dev/null +++ b/src/app/api/protected/isLoggedIn/route.js @@ -0,0 +1,11 @@ +import { NextResponse } from "next/server"; +import { verifyToken } from "@/src/lib/middleware/auth"; + +export async function GET(request) { + const auth = verifyToken(request); + if (auth.error) { + return NextResponse.json({ error: auth.error }, { status: auth.status }); + } + + return NextResponse.json({ login: true, message: "User Logged In" }, { status: 200 }); +} diff --git a/src/app/api/protected/logout/route.js b/src/app/api/protected/logout/route.js new file mode 100644 index 0000000..8cf2780 --- /dev/null +++ b/src/app/api/protected/logout/route.js @@ -0,0 +1,14 @@ +import { NextResponse } from "next/server"; +import { verifyToken } from "@/src/lib/middleware/auth"; + +export async function GET(request) { + const auth = verifyToken(request); + if (auth.error) { + return NextResponse.json({ error: auth.error }, { status: auth.status }); + } + + const response = NextResponse.json({ message: "Logout successful" }, { status: 200 }); + response.cookies.set("AuthToken", "", { maxAge: 0, path: "/" }); + + return response; +} diff --git a/src/app/api/user/login/route.js b/src/app/api/user/login/route.js new file mode 100644 index 0000000..8be5fdd --- /dev/null +++ b/src/app/api/user/login/route.js @@ -0,0 +1,48 @@ +import { NextResponse } from "next/server"; +import bcrypt from "bcryptjs"; +import jwt from "jsonwebtoken"; +import dbConnect from "@/src/lib/db"; +import User from "@/src/lib/models/user"; + +export async function POST(request) { + try { + await dbConnect(); + + const { email, password } = await request.json(); + + if (!email) { + return NextResponse.json({ message: "Email Required" }, { status: 400 }); + } + if (!password) { + return NextResponse.json({ message: "Password Required" }, { status: 400 }); + } + + const user = await User.findOne({ email }); + if (!user) { + return NextResponse.json({ message: "User Not Found" }, { status: 401 }); + } + + const passwordMatch = await bcrypt.compare(password, user.password); + if (!passwordMatch) { + return NextResponse.json({ message: "Authentication failed: Wrong Password" }, { status: 401 }); + } + + const token = jwt.sign( + { userId: user._id, username: user.username, email }, + process.env.SECERT_KEY, + { expiresIn: "24h" } + ); + + return NextResponse.json( + { + userId: user._id, + username: user.username, + email: user.email, + AuthToken: token, + }, + { status: 200 } + ); + } catch (err) { + return NextResponse.json({ message: "Unable to authenticate user", error: err.message }, { status: 500 }); + } +} diff --git a/src/app/api/user/register/route.js b/src/app/api/user/register/route.js new file mode 100644 index 0000000..18e51d2 --- /dev/null +++ b/src/app/api/user/register/route.js @@ -0,0 +1,61 @@ +import { NextResponse } from "next/server"; +import bcrypt from "bcryptjs"; +import jwt from "jsonwebtoken"; +import dbConnect from "@/src/lib/db"; +import User from "@/src/lib/models/user"; + +const passwordValidater = (password) => { + const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/; + return regex.test(password); +}; + +export async function POST(request) { + try { + await dbConnect(); + + const { username, email, password } = await request.json(); + + if (!username) { + return NextResponse.json({ message: "Username Required" }, { status: 400 }); + } + if (!email) { + return NextResponse.json({ message: "Email Required" }, { status: 400 }); + } + if (!password) { + return NextResponse.json({ message: "Password Required" }, { status: 400 }); + } + + if (!passwordValidater(password)) { + return NextResponse.json( + { message: "Password must contain at least 8 characters, 1 uppercase, 1 lowercase, 1 number, and 1 special character" }, + { status: 400 } + ); + } + + const existingUser = await User.findOne({ email }); + if (existingUser) { + return NextResponse.json({ message: "User already exists with given email Id" }, { status: 409 }); + } + + const hashedPassword = await bcrypt.hash(password, 10); + const userData = await User.create({ username, email, password: hashedPassword }); + + const token = jwt.sign( + { userId: userData._id, username, email }, + process.env.SECERT_KEY, + { expiresIn: "24h" } + ); + + return NextResponse.json( + { + userId: userData._id, + username: userData.username, + email: userData.email, + AuthToken: token, + }, + { status: 201 } + ); + } catch (err) { + return NextResponse.json({ message: "Unable to register user", error: err.message }, { status: 500 }); + } +} diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx new file mode 100644 index 0000000..699c154 --- /dev/null +++ b/src/app/dashboard/page.tsx @@ -0,0 +1,251 @@ +"use client"; + +import { useEffect, useState, useCallback } from "react"; +import { useRouter } from "next/navigation"; +import axios from "axios"; +import toast from "react-hot-toast"; + +import { NavBar } from "@/src/components"; +import SearchBar from "@/src/components/dashboard/SearchBar"; +import ProjectGrid from "@/src/components/dashboard/ProjectGrid"; +import CreateProjectDialog from "@/src/components/dashboard/CreateProjectDialog"; + +interface Project { + _id: string; + title: string; + description?: string; + programmingLanguage: string; + visibility: "public" | "private"; + updatedAt: string; + createdAt: string; + owner: { _id: string; username: string }; + archived: boolean; +} + +export default function DashboardPage() { + const router = useRouter(); + const [projects, setProjects] = useState([]); + const [filtered, setFiltered] = useState([]); + const [loading, setLoading] = useState(true); + const [showCreate, setShowCreate] = useState(false); + const [search, setSearch] = useState(""); + + const fetchProjects = useCallback(async () => { + try { + const res = await axios.get("/api/projects"); + if (res.data.success) { + setProjects(res.data.data); + setFiltered(res.data.data); + } + } catch { + toast.error("Failed to load projects"); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + fetchProjects(); + }, [fetchProjects]); + + useEffect(() => { + if (!search) { + setFiltered(projects); + } else { + const q = search.toLowerCase(); + setFiltered( + projects.filter( + (p) => + p.title.toLowerCase().includes(q) || + p.description?.toLowerCase().includes(q) || + p.programmingLanguage.toLowerCase().includes(q) + ) + ); + } + }, [search, projects]); + + const handleCreate = async (data: { + title: string; + description: string; + programmingLanguage: string; + visibility: "public" | "private"; + }) => { + try { + const res = await axios.post("/api/projects", data); + if (res.data.success) { + router.push(`/editor/${res.data.data._id}`); + } + } catch { + toast.error("Failed to create project"); + } + }; + + const handleRename = async (id: string, title: string) => { + try { + await axios.put(`/api/projects/${id}`, { title }); + toast.success("Project renamed"); + fetchProjects(); + } catch { + toast.error("Failed to rename project"); + } + }; + + const handleArchive = async (id: string) => { + try { + await axios.put(`/api/projects/${id}`, { archived: true }); + toast.success("Project archived"); + fetchProjects(); + } catch { + toast.error("Failed to archive project"); + } + }; + + const handleDelete = async (id: string) => { + if (!window.confirm("Delete this project permanently? This cannot be undone.")) return; + try { + await axios.delete(`/api/projects/${id}`); + toast.success("Project deleted"); + fetchProjects(); + } catch { + toast.error("Failed to delete project"); + } + }; + + const handleDuplicate = async (id: string) => { + try { + await axios.post(`/api/projects/${id}/duplicate`); + toast.success("Project duplicated"); + fetchProjects(); + } catch { + toast.error("Failed to duplicate project"); + } + }; + + return ( + <> + +
+
+
+

Dashboard

+

Manage your projects

+
+
+ + +
+
+ + {loading ? ( +
+ {[1, 2, 3, 4, 5, 6].map((i) => ( +
+
+
+
+
+ ))} +
+ ) : ( + + )} +
+ + setShowCreate(false)} + onCreate={handleCreate} + /> + + ); +} + +const styles: Record = { + page: { + padding: "80px 32px 32px", + maxWidth: "1200px", + margin: "0 auto", + minHeight: "100vh", + background: "var(--bg-primary)", + }, + top: { + display: "flex", + justifyContent: "space-between", + alignItems: "center", + marginBottom: "32px", + flexWrap: "wrap", + gap: "16px", + }, + topLeft: {}, + topRight: { + display: "flex", + alignItems: "center", + gap: "12px", + }, + heading: { + fontSize: "28px", + fontWeight: 700, + color: "var(--text-primary)", + margin: 0, + }, + subtitle: { + fontSize: "14px", + color: "var(--text-muted)", + margin: "4px 0 0", + }, + createBtn: { + display: "flex", + alignItems: "center", + gap: "6px", + padding: "10px 20px", + background: "var(--brand)", + color: "#fff", + border: "none", + borderRadius: "8px", + fontSize: "14px", + fontWeight: 500, + cursor: "pointer", + whiteSpace: "nowrap", + }, + loading: { + display: "grid", + gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))", + gap: "16px", + }, + skeleton: { + background: "var(--bg-secondary)", + borderRadius: "12px", + border: "1px solid var(--border-color)", + overflow: "hidden", + }, + skelHeader: { + height: "44px", + background: "linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%)", + backgroundSize: "200% 100%", + animation: "shimmer 1.5s infinite", + }, + skelBody: { + height: "80px", + background: "linear-gradient(90deg, #f5f5f5 25%, #e8e8e8 50%, #f5f5f5 75%)", + backgroundSize: "200% 100%", + animation: "shimmer 1.5s infinite", + }, + skelFooter: { + height: "36px", + background: "linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%)", + backgroundSize: "200% 100%", + animation: "shimmer 1.5s infinite", + }, +}; diff --git a/src/app/editor/[projectId]/page.tsx b/src/app/editor/[projectId]/page.tsx new file mode 100644 index 0000000..ea980a2 --- /dev/null +++ b/src/app/editor/[projectId]/page.tsx @@ -0,0 +1,12 @@ +"use client"; + +import dynamic from "next/dynamic"; + +const EditorLayout = dynamic( + () => import("@/src/components/Editor/Editor"), + { ssr: false } +); + +export default function EditorPage({ params }: { params: { projectId: string } }) { + return ; +} diff --git a/src/app/editor/page.tsx b/src/app/editor/page.tsx new file mode 100644 index 0000000..f4e95a9 --- /dev/null +++ b/src/app/editor/page.tsx @@ -0,0 +1,46 @@ +"use client"; + +import { useEffect } from "react"; +import { useRouter } from "next/navigation"; + +const ADJECTIVES = [ + "cool", "fast", "bright", "quiet", "swift", "bold", "calm", + "eager", "fierce", "gentle", "happy", "keen", "lively", "mighty", + "neat", "proud", "quick", "rare", "sharp", "tough", "vivid", + "warm", "zesty", "agile", "brave", "crisp", +]; +const NOUNS = [ + "alpha", "beta", "delta", "echo", "falcon", "gamma", "hawk", + "jade", "kappa", "lion", "nova", "omega", "pixel", "quantum", + "raven", "sigma", "tiger", "ultra", "vertex", "wave", "zeta", + "blitz", "cipher", "drift", "ember", "frost", +]; + +function randomName(): string { + const a = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)]; + const n = NOUNS[Math.floor(Math.random() * NOUNS.length)]; + return `${a}-${n}`; +} + +export default function EditorNew() { + const router = useRouter(); + + useEffect(() => { + fetch("/api/projects", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ title: randomName() }), + }) + .then((res) => res.json()) + .then((data) => { + if (data.success) { + router.replace(`/editor/${data.data._id}`); + } + }) + .catch(() => { + router.replace("/dashboard"); + }); + }, [router]); + + return null; +} diff --git a/src/app/layout.jsx b/src/app/layout.jsx new file mode 100644 index 0000000..b42fa64 --- /dev/null +++ b/src/app/layout.jsx @@ -0,0 +1,17 @@ +import "@/src/index.css"; +import { ThemeProvider } from "@/src/context/ThemeContext"; + +export const metadata = { + title: "CodeLab", + description: "Collaborative online real-time code editor", +}; + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ); +} diff --git a/src/app/login/page.jsx b/src/app/login/page.jsx new file mode 100644 index 0000000..d4b2906 --- /dev/null +++ b/src/app/login/page.jsx @@ -0,0 +1,7 @@ +import Login from "@/src/components/Login/Login"; + +export const dynamic = "force-dynamic"; + +export default function LoginPage() { + return ; +} diff --git a/src/app/not-found.jsx b/src/app/not-found.jsx new file mode 100644 index 0000000..2df1e32 --- /dev/null +++ b/src/app/not-found.jsx @@ -0,0 +1,27 @@ +import Link from "next/link"; + +export default function NotFound() { + return ( +
+

404

+

Page Not Found

+

+ The page you are looking for does not exist. +

+ + Go Home + +
+ ); +} diff --git a/src/app/page.jsx b/src/app/page.jsx new file mode 100644 index 0000000..9f43d9f --- /dev/null +++ b/src/app/page.jsx @@ -0,0 +1,372 @@ +"use client"; + +import { useEffect } from "react"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { NavBar } from "@/src/components"; + +const BRAND = "#5f5fff"; +const BRAND_GLOW = "rgba(95, 95, 255, 0.08)"; +const BORDER = "var(--border-subtle)"; +const TEXT_MUTED = "var(--text-muted)"; + +export default function Landing() { + const router = useRouter(); + + useEffect(() => { + const cookies = document.cookie.split("; "); + if (cookies.find((c) => c.startsWith("AuthToken="))) { + router.replace("/dashboard"); + } + }, [router]); + + return ( +
+ + + {/* ── Hero ── */} +
+ + v2.0 — Now in public beta + + +

+ CODE IN{" "} + UNISON +

+ +

+ The collaborative engine for technical interviews, pair programming, + and engineering education. Real-time, zero latency. +

+ +
+ + Launch Editor + + + Open Dashboard + +
+ + {/* ── Editor mock ── */} +
+
+ + + + + project_delta.py + +
+
+ import{" "} + asyncio +
+ from{" "} + collab{" "} + import{" "} + Session +
+
+ session{" "} + ={" "} + await{" "} + Session( + "delta") +
+ await{" "} + session. + sync() +
+
+ # 3 collaborators connected +
+
+
+ {["#5f5fff", "#ffbd2e", "#28c840"].map((c, i) => ( +
+ ))} +
+ + 3 users editing project_delta.py + +
+
+
+ + {/* ── Features ── */} +
+
+

+ {'// Built for engineering teams'} +

+ +
+ {[ + { + title: "Zero-latency sync", + desc: "CRDT-backed editing keeps every keystroke in lockstep across continents.", + num: "01", + }, + { + title: "Live execution", + desc: "Run 40+ languages in isolated containers — no setup, no sandbox surprises.", + num: "02", + }, + { + title: "Interview-grade", + desc: "Persistent projects with real-time sync for seamless team collaboration.", + num: "03", + }, + ].map((f) => ( +
+
+ {f.num} +
+

+ {f.title} +

+

+ {f.desc} +

+
+ ))} +
+
+
+ + {/* ── CTA ── */} +
+

+ Start coding together{" "} + in seconds. +

+

+ No installs. No accounts for guests. Just share a link. +

+ + Create a Room + +
+ + {/* ── Footer ── */} +
+ © {new Date().getFullYear()} CodeLab — Collaborative real-time code editor +
+
+ ); +} diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx new file mode 100644 index 0000000..f7aec2e --- /dev/null +++ b/src/app/profile/page.tsx @@ -0,0 +1,179 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import axios from "axios"; + +import { NavBar } from "@/src/components"; + +interface ProfileData { + username: string; + email: string; + userId: string; + joinedAt?: string; +} + +export default function ProfilePage() { + const router = useRouter(); + const [profile, setProfile] = useState(null); + const [loading, setLoading] = useState(true); + const [projectCount, setProjectCount] = useState(0); + + useEffect(() => { + const cookies = document.cookie.split("; "); + const username = cookies.find((c) => c.startsWith("username="))?.split("=")[1]; + const email = cookies.find((c) => c.startsWith("email="))?.split("=")[1]; + const userId = cookies.find((c) => c.startsWith("userId="))?.split("=")[1]; + + if (!username) { + router.push("/login"); + return; + } + + setProfile({ + username: decodeURIComponent(username), + email: decodeURIComponent(email || ""), + userId: userId || "", + }); + + axios.get("/api/projects").then((res) => { + if (res.data.success) { + setProjectCount(res.data.data.length); + } + }).finally(() => setLoading(false)); + }, [router]); + + if (loading || !profile) { + return ( + <> + +
+
+
+ + ); + } + + return ( + <> + +
+
+
+ {profile.username.charAt(0).toUpperCase()} +
+

{profile.username}

+

{profile.email}

+ +
+
+ {projectCount} + Projects +
+
+ +
+ + Go to Dashboard + +
+
+
+ + ); +} + +const styles: Record = { + page: { + padding: "100px 24px 32px", + maxWidth: "600px", + margin: "0 auto", + minHeight: "100vh", + background: "var(--bg-primary)", + display: "flex", + justifyContent: "center", + }, + loader: { + display: "flex", + justifyContent: "center", + alignItems: "center", + height: "100vh", + }, + spinner: { + width: "32px", + height: "32px", + border: "3px solid #e5e7eb", + borderTopColor: "var(--brand)", + borderRadius: "50%", + animation: "spin 0.8s linear infinite", + }, + card: { + background: "var(--bg-secondary)", + borderRadius: "16px", + border: "1px solid var(--border-color)", + padding: "40px 32px", + width: "100%", + display: "flex", + flexDirection: "column", + alignItems: "center", + textAlign: "center", + }, + avatarLarge: { + width: "80px", + height: "80px", + borderRadius: "50%", + backgroundColor: "var(--brand)", + color: "#fff", + fontSize: "32px", + fontWeight: 700, + display: "flex", + alignItems: "center", + justifyContent: "center", + marginBottom: "16px", + }, + name: { + fontSize: "24px", + fontWeight: 700, + color: "var(--text-primary)", + margin: 0, + }, + email: { + fontSize: "14px", + color: "var(--text-muted)", + margin: "4px 0 24px", + }, + stats: { + display: "flex", + gap: "32px", + marginBottom: "28px", + }, + stat: { + display: "flex", + flexDirection: "column", + alignItems: "center", + }, + statValue: { + fontSize: "24px", + fontWeight: 700, + color: "var(--text-primary)", + }, + statLabel: { + fontSize: "13px", + color: "var(--text-muted)", + }, + actions: { + display: "flex", + gap: "12px", + }, + actionBtn: { + display: "inline-flex", + padding: "10px 24px", + background: "var(--brand)", + color: "#fff", + borderRadius: "8px", + fontSize: "14px", + fontWeight: 500, + textDecoration: "none", + }, +}; diff --git a/src/app/register/page.jsx b/src/app/register/page.jsx new file mode 100644 index 0000000..54e11e5 --- /dev/null +++ b/src/app/register/page.jsx @@ -0,0 +1,7 @@ +import SignUp from "@/src/components/SignUp/SignUp"; + +export const dynamic = "force-dynamic"; + +export default function RegisterPage() { + return ; +} diff --git a/Frontend/src/assets/background.svg b/src/assets/background.svg similarity index 100% rename from Frontend/src/assets/background.svg rename to src/assets/background.svg diff --git a/Frontend/src/assets/icon-logo-light.png b/src/assets/icon-logo-light.png similarity index 100% rename from Frontend/src/assets/icon-logo-light.png rename to src/assets/icon-logo-light.png diff --git a/Frontend/src/assets/logo--codelab.png b/src/assets/logo--codelab.png similarity index 100% rename from Frontend/src/assets/logo--codelab.png rename to src/assets/logo--codelab.png diff --git a/Frontend/src/assets/logo-Dark.png b/src/assets/logo-Dark.png similarity index 100% rename from Frontend/src/assets/logo-Dark.png rename to src/assets/logo-Dark.png diff --git a/Frontend/src/assets/logo-icon-dark.png b/src/assets/logo-icon-dark.png similarity index 100% rename from Frontend/src/assets/logo-icon-dark.png rename to src/assets/logo-icon-dark.png diff --git a/Frontend/src/assets/logo-light.png b/src/assets/logo-light.png similarity index 100% rename from Frontend/src/assets/logo-light.png rename to src/assets/logo-light.png diff --git a/Frontend/src/assets/plus.svg b/src/assets/plus.svg similarity index 100% rename from Frontend/src/assets/plus.svg rename to src/assets/plus.svg diff --git a/Frontend/src/assets/right-arrow.svg b/src/assets/right-arrow.svg similarity index 100% rename from Frontend/src/assets/right-arrow.svg rename to src/assets/right-arrow.svg diff --git a/Frontend/src/assets/share.svg b/src/assets/share.svg similarity index 100% rename from Frontend/src/assets/share.svg rename to src/assets/share.svg diff --git a/src/components/Editor/Editor.jsx b/src/components/Editor/Editor.jsx new file mode 100644 index 0000000..8ec20f7 --- /dev/null +++ b/src/components/Editor/Editor.jsx @@ -0,0 +1,333 @@ +"use client"; + +import { useState, useCallback, useEffect, useMemo, useRef } from "react"; +import dynamic from "next/dynamic"; +import { Toaster } from "react-hot-toast"; +import { io } from "socket.io-client"; + +import notify from "../../services/toast"; +import { useTheme } from "../../context/ThemeContext"; +import style from "./Editor.module.scss"; +import NavBar from "../NavBar/NavBar"; + +const MonacoEditor = dynamic(() => import("@monaco-editor/react"), { ssr: false }); + +const LANGUAGES = [ + "javascript", "typescript", "python", "html", "css", + "rust", "go", "java", "cpp", "ruby", "php", +]; + +const EditorLayout = ({ projectId }) => { + const { theme } = useTheme(); + const socketRef = useMemo(() => io({ reconnectionAttempts: 2 }), []); + const [code, setCode] = useState("// your code..."); + const [project, setProject] = useState(null); + const [language, setLanguage] = useState("javascript"); + const [clients, setClients] = useState([]); + const [messages, setMessages] = useState([]); + const [chatDraft, setChatDraft] = useState(""); + const [editingTitle, setEditingTitle] = useState(false); + const [titleDraft, setTitleDraft] = useState(""); + const [saving, setSaving] = useState(false); + const chatEndRef = useRef(null); + const titleRef = useRef(null); + + const monacoTheme = theme === "dark" ? "vs-dark" : "light"; + + const cookies = document.cookie; + const username = useMemo(() => { + const match = cookies?.split("; ").find((c) => c.startsWith("username=")); + return match ? match.split("=")[1] : "Unknown User"; + }, []); + + const editorDidMount = useCallback((editor) => { + editor.focus(); + }, []); + + useEffect(() => { + document.title = "Editor"; + }, []); + + useEffect(() => { + chatEndRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [messages]); + + useEffect(() => { + fetch(`/api/projects/${projectId}`) + .then((r) => r.json()) + .then((res) => { + if (res.success) { + setProject(res.data); + setCode(res.data.code || "// your code..."); + setLanguage(res.data.programmingLanguage || "javascript"); + } + }) + .catch(() => {}); + }, [projectId]); + + const handleErrors = () => { + notify("Socket connection failed, try again later."); + }; + + const leave = () => { + if (socketRef) { + socketRef.emit("leave", projectId); + } + }; + + useEffect(() => { + socketRef.on("connect_error", () => handleErrors()); + socketRef.on("connect_failed", () => handleErrors()); + socketRef.emit("JOIN", { roomId: projectId, username }); + + socketRef.on("JOINED", ({ code: incomingCode, clients: connected }) => { + if (incomingCode) setCode(incomingCode); + if (connected) setClients(connected); + }); + + socketRef.on("code change", ({ newCode }) => { + setCode(newCode); + }); + + socketRef.on("user joined", ({ username: u, clients: connected }) => { + if (connected) setClients(connected); + }); + + socketRef.on("disconnected", ({ username: u, clients: connected }) => { + if (connected) setClients(connected); + }); + + socketRef.on("chat", ({ username: u, message }) => { + setMessages((prev) => [...prev, { username: u, message }]); + }); + + window.addEventListener("beforeunload", leave); + return () => { + window.removeEventListener("beforeunload", leave); + }; + }, [projectId]); + + const options = { + selectOnLineNumbers: true, + minimap: { enabled: false }, + fontSize: 14, + fontFamily: "'Cascadia Code', 'Fira Code', 'JetBrains Mono', Consolas, monospace", + lineNumbers: "on", + renderWhitespace: "selection", + bracketPairColorization: { enabled: true }, + autoClosingBrackets: "always", + scrollBeyondLastLine: false, + padding: { top: 16, bottom: 16 }, + smoothScrolling: true, + cursorBlinking: "smooth", + cursorSmoothCaretAnimation: "on", + accessibilitySupport: "on", + ariaLabel: "Code editor", + }; + + const onCodeChange = (newValue) => { + setCode(newValue); + if (socketRef) { + socketRef.emit("code change", { roomId: projectId, newCode: newValue }); + } + }; + + const handleLangChange = (e) => { + const newLang = e.target.value; + setLanguage(newLang); + fetch(`/api/projects/${projectId}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ programmingLanguage: newLang }), + }).catch(() => notify("Failed to update language")); + }; + + const startEditingTitle = () => { + setTitleDraft(project?.title || ""); + setEditingTitle(true); + }; + + const saveTitle = () => { + const trimmed = titleDraft.trim(); + if (!trimmed) { + setEditingTitle(false); + return; + } + setProject((prev) => ({ ...prev, title: trimmed })); + setEditingTitle(false); + fetch(`/api/projects/${projectId}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ title: trimmed }), + }).catch(() => notify("Failed to update title")); + }; + + const handleTitleKey = (e) => { + if (e.key === "Enter") saveTitle(); + if (e.key === "Escape") setEditingTitle(false); + }; + + const handleSave = async () => { + setSaving(true); + try { + await fetch(`/api/projects/${projectId}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ code }), + }); + notify("Saved"); + } catch { + notify("Failed to save"); + } finally { + setSaving(false); + } + }; + + const handleChatSend = (e) => { + e.preventDefault(); + if (!chatDraft.trim()) return; + socketRef.emit("chat", { roomId: projectId, username, message: chatDraft.trim() }); + setChatDraft(""); + }; + + const initial = username ? username.charAt(0).toUpperCase() : "?"; + + return ( + <> + + +
+
+ +
+ + {/* ── Right panel ── */} + +
+ + + + ); +}; + +export default EditorLayout; diff --git a/src/components/Editor/Editor.module.scss b/src/components/Editor/Editor.module.scss new file mode 100644 index 0000000..0a9a77a --- /dev/null +++ b/src/components/Editor/Editor.module.scss @@ -0,0 +1,272 @@ +.editor { + flex: 1; + min-width: 0; + position: relative; +} + +.editorWrap { + display: flex; + height: calc(100vh - 70px); + padding-top: 70px; + background: var(--bg-primary); + transition: background 0.2s ease; +} + +/* ── Right panel ── */ +.panel { + width: 280px; + flex-shrink: 0; + background: var(--bg-secondary); + border-left: 1px solid var(--border-color); + display: flex; + flex-direction: column; + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track); + transition: background 0.2s ease, border-color 0.2s ease; +} + +.panel::-webkit-scrollbar { + width: 4px; +} + +.panel::-webkit-scrollbar-track { + background: var(--scrollbar-track); +} + +.panel::-webkit-scrollbar-thumb { + background: var(--scrollbar-thumb); + border-radius: 2px; +} + +.panelSection { + padding: 12px 16px; + border-bottom: 1px solid var(--border-color); + transition: border-color 0.2s ease; +} + +.panelSection:last-child { + border-bottom: none; + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; +} + +.panelLabel { + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--text-muted); + margin-bottom: 8px; +} + +.panelLabelRow { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 8px; +} + +.saveBtnSmall { + background: none; + border: 1px solid var(--border-color); + color: var(--text-muted); + font-size: 10px; + font-weight: 600; + padding: 2px 10px; + border-radius: 4px; + cursor: pointer; + font-family: inherit; + transition: border-color 0.15s, color 0.15s, background 0.15s; +} + +.saveBtnSmall:hover { + border-color: var(--brand); + color: var(--brand); + background: var(--brand-light); +} + +.saveBtnSmallSaving { + opacity: 0.4; + pointer-events: none; +} + +.panelValue { + font-size: 13px; + color: var(--text-primary); +} + +.projectNameEdit { + font-size: 13px; + color: var(--text-primary); + background: var(--bg-input); + border: 1px solid var(--brand); + padding: 4px 8px; + border-radius: 4px; + outline: none; + width: 100%; + font-family: inherit; + box-sizing: border-box; +} + +.projectNameDisplay { + font-size: 13px; + color: var(--text-primary); + cursor: text; + padding: 4px 8px; + border-radius: 4px; + border: 1px solid transparent; + transition: border-color 0.15s, background 0.15s; +} + +.projectNameDisplay:hover { + border-color: var(--border-color); + background: var(--bg-tertiary); +} + +.langSelect { + width: 100%; + background: var(--bg-input); + border: 1px solid var(--border-color); + color: var(--text-primary); + font-size: 13px; + padding: 6px 10px; + border-radius: 4px; + outline: none; + cursor: pointer; + font-family: inherit; + transition: border-color 0.15s; + appearance: auto; +} + +.langSelect:focus { + border-color: var(--brand); +} + +/* ── Collaborators (stacked circles) ── */ +.collabCount { + font-size: 12px; + color: var(--text-muted); +} + +.collabStack { + display: flex; + align-items: center; +} + +.collabCircle { + width: 28px; + height: 28px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 10px; + font-weight: 700; + flex-shrink: 0; + border: 2px solid; + position: relative; + cursor: default; + transition: transform 0.15s; +} + +.collabCircle:hover { + transform: translateY(-2px); +} + +/* ── Chat ── */ +.chatMessages { + flex: 1; + overflow-y: auto; + overflow-x: hidden; + display: flex; + flex-direction: column; + gap: 6px; + margin-bottom: 8px; + scrollbar-width: thin; + scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track); +} + +.chatMessages::-webkit-scrollbar { + width: 4px; +} + +.chatMessages::-webkit-scrollbar-track { + background: var(--scrollbar-track); +} + +.chatMessages::-webkit-scrollbar-thumb { + background: var(--scrollbar-thumb); + border-radius: 2px; +} + +.chatMsg { + font-size: 12px; + line-height: 1.4; +} + +.chatName { + font-weight: 600; +} + +.chatText { + color: var(--text-secondary); +} + +.chatEmpty { + color: var(--text-faint); + font-size: 11px; + text-align: center; + padding: 12px 0; +} + +.chatInputWrap { + display: flex; + gap: 6px; + margin-top: auto; +} + +.chatInput { + flex: 1; + background: var(--bg-input); + border: 1px solid var(--border-color); + color: var(--text-primary); + font-size: 12px; + padding: 7px 10px; + border-radius: 4px; + outline: none; + font-family: inherit; + transition: border-color 0.15s; +} + +.chatInput::placeholder { + color: var(--text-faint); +} + +.chatInput:focus { + border-color: var(--brand); +} + +.chatSend { + background: var(--brand); + color: white; + border: none; + border-radius: 4px; + padding: 0 12px; + cursor: pointer; + font-size: 12px; + font-weight: 600; + transition: background 0.15s; + font-family: inherit; +} + +.chatSend:hover { + background: var(--brand-hover); +} + +.chatSend:focus-visible { + outline: 2px solid var(--brand); + outline-offset: 2px; +} diff --git a/Frontend/src/components/Login/Login.jsx b/src/components/Login/Login.jsx similarity index 66% rename from Frontend/src/components/Login/Login.jsx rename to src/components/Login/Login.jsx index a241f41..8691451 100644 --- a/Frontend/src/components/Login/Login.jsx +++ b/src/components/Login/Login.jsx @@ -1,14 +1,14 @@ -import React from "react"; +"use client"; + +import { useEffect } from "react"; import axios from "axios"; -import { useCookies } from "react-cookie"; -import { Link, useNavigate } from "react-router-dom"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; import "./Login.scss"; const Login = () => { - const [cookies, setCookie] = useCookies(["user"]); - const navigate = useNavigate(); - document.title = "Login"; + const router = useRouter(); const login = (e) => { e.preventDefault(); @@ -17,34 +17,42 @@ const Login = () => { const password = e.target.password.value; axios - .post("http://localhost:8000/user/login", { - email: email, - password: password, - }) + .post( + "/api/user/login", + { + email: email, + password: password, + }, + ) .then((res) => { - setCookie("email", email, { path: "/" }); - setCookie("username", res.data.username, { path: "/" }); - setCookie("userId", res.data.userId, { path: "/" }); + document.cookie = `email=${email}; path=/`; + document.cookie = `username=${res.data.username}; path=/`; + document.cookie = `userId=${res.data.userId}; path=/`; + document.cookie = `AuthToken=${res.data.AuthToken}; path=/`; - navigate("/"); + router.push("/dashboard"); }) .catch((err) => { - if(err.request.status == 400){ - alert("Email or Passwrod Missing.") + if (err.request.status == 400) { + alert("Email or Password Missing."); } - if(err.request.status == 401){ - alert("Authentication failed: Wrong Email or Password.") + if (err.request.status == 401) { + alert("Authentication failed: Wrong Email or Password."); } - if(err.request.status == 500){ - alert("Ops!!! Server Error.") + if (err.request.status == 500) { + alert("Ops!!! Server Error."); } }); e.target.reset(); }; + useEffect(() => { + document.title = "Login"; + }, []); + return (
@@ -86,13 +94,13 @@ const Login = () => { Remember me
- Forget Password? + Forget Password?

- New on our platform? Create an account + New on our platform? Create an account

diff --git a/Frontend/src/components/Login/Login.scss b/src/components/Login/Login.scss similarity index 70% rename from Frontend/src/components/Login/Login.scss rename to src/components/Login/Login.scss index d924981..1983eb0 100644 --- a/Frontend/src/components/Login/Login.scss +++ b/src/components/Login/Login.scss @@ -4,6 +4,8 @@ align-items: center; width: 100%; height: 100%; + min-height: 100vh; + background: var(--bg-primary); .login{ display: flex; @@ -17,7 +19,7 @@ h1{ font-size: 24px; font-weight: 500; - color: rgba(47, 43, 61, 0.9); + color: var(--text-primary); line-height: 38px; padding-bottom: 5px; } @@ -26,11 +28,11 @@ font-size: 15px; font-weight: 500; line-height: 22px; - color: rgba(47, 43, 61, 0.7); + color: var(--text-secondary); } - + .example{ - background-color: #e8e7fd; + background-color: var(--brand-light); padding: 10px; border-radius: 5px; margin: 20px 0px; @@ -40,7 +42,7 @@ font-size: 13px; font-weight: 500; line-height: 20px; - color: #7367f0; + color: var(--brand); } } @@ -49,7 +51,7 @@ font-size: 13px; font-weight: 500; line-height: 20px; - color: rgba(47, 43, 61, 0.9); + color: var(--text-primary); } .input-tag{ @@ -60,17 +62,22 @@ font-size: 15px; line-height: 24px; font-weight: 500; - color: rgba(47, 43, 61, 0.9); + color: var(--text-primary); + background: var(--bg-input); - border: 1px solid rgba(47, 43, 61, 0.3); + border: 1px solid var(--border-color); border-radius: 5px; + &::placeholder{ + color: var(--text-faint); + } + &:hover{ - border-color: rgba(47, 43, 61, 0.6); + border-color: var(--border-hover); } &:focus-visible{ - border-color: #7367f0; + border-color: var(--brand); } } @@ -84,7 +91,7 @@ justify-content: center; align-items: center; font-size: 15px; - color: rgba(47, 43, 61, 0.9); + color: var(--text-primary); input{ width: 16px; @@ -94,7 +101,7 @@ } a{ - color: #7367f0; + color: var(--brand); text-decoration: none; font-weight: 500; } @@ -104,7 +111,7 @@ width: 100%; text-align: center; padding: 8px; - background-color: #7367f0; + background: var(--brand); border-radius: 5px; border: 0px; margin: 20px 0px; @@ -112,6 +119,13 @@ font-size: 15px; line-height: 22px; color: #ffffff; + cursor: pointer; + font-weight: 600; + transition: background 0.15s; + + &:hover{ + background: var(--brand-hover); + } } } @@ -121,14 +135,14 @@ font-size: 15px; line-height: 22px; font-weight: 400; - color: rgba(47, 43, 61, 0.9); + color: var(--text-primary); a{ text-decoration: none; font-weight: 500; - color: #7367f0; + color: var(--brand); } } } } -} \ No newline at end of file +} diff --git a/src/components/NavBar/NavBar.module.scss b/src/components/NavBar/NavBar.module.scss new file mode 100644 index 0000000..9393fc6 --- /dev/null +++ b/src/components/NavBar/NavBar.module.scss @@ -0,0 +1,245 @@ +.nav{ + width: 100%; + background: var(--bg-primary); + z-index: 5; + padding: 10px 24px; + position: fixed; + top: 0; + left: 0; + box-sizing: border-box; + border-bottom: 1px solid var(--border-color); + transition: background 0.2s ease, border-color 0.2s ease; + + .nav__menu{ + height: 50px; + display: flex; + justify-content: space-between; + align-items: center; + gap: 24px; + + > a { + display: flex; + align-items: center; + flex-shrink: 0; + + img{ + width: 120px; + display: block; + } + } + } + + .nav__list{ + display: flex; + align-items: center; + gap: 12px; + flex-shrink: 0; + + .new_file{ + a { + text-decoration: none; + } + + div{ + display: flex; + align-items: center; + gap: 6px; + padding: 6px 16px 6px 10px; + cursor: pointer; + font-size: 14px; + font-weight: 600; + transition: transform 150ms, box-shadow 150ms; + white-space: nowrap; + color: var(--brand); + border: 2px solid var(--brand); + border-radius: 50px; + + svg{ + width: 14px; + height: 14px; + flex-shrink: 0; + } + + &:hover{ + transform: translateY(-1px); + } + + &:active{ + transform: translateY(0); + } + } + } + + .share{ + position: relative; + + .img{ + display: flex; + align-items: center; + gap: 6px; + padding: 6px 16px 6px 10px; + cursor: pointer; + font-size: 14px; + font-weight: 600; + white-space: nowrap; + color: var(--brand); + border: 2px solid var(--brand); + border-radius: 50px; + transition: transform 150ms, box-shadow 150ms; + + svg{ + width: 18px; + flex-shrink: 0; + } + + &:hover{ + transform: translateY(-1px); + } + + &:active{ + transform: translateY(0); + } + } + + .shareNav{ + position: absolute; + width: 360px; + right: 0; + top: calc(100% + 8px); + padding: 24px; + border-radius: 10px; + background: var(--bg-elevated); + border: 1px solid var(--border-color); + box-shadow: var(--shadow-md); + z-index: 10; + + p{ + font-size: 14px; + color: var(--text-secondary); + margin: 0 0 12px; + } + + div{ + display: flex; + gap: 8px; + align-items: center; + + input{ + flex: 1; + padding: 8px 12px; + border-radius: 6px; + outline: none; + font-size: 13px; + border: 1px solid var(--border-color); + background: var(--bg-input); + color: var(--text-primary); + min-width: 0; + } + + span{ + display: flex; + align-items: center; + gap: 4px; + color: white; + padding: 8px 16px; + border-radius: 8px; + background-color: var(--brand); + font-size: 13px; + font-weight: 500; + cursor: pointer; + flex-shrink: 0; + + svg{ + width: 16px; + height: 16px; + } + + &:active{ + transform: scale(0.96); + } + } + } + } + } + + .themeToggle { + display: flex; + align-items: center; + justify-content: center; + width: 34px; + height: 34px; + border: 1px solid var(--border-color); + border-radius: 50%; + background: var(--bg-secondary); + color: var(--text-secondary); + cursor: pointer; + transition: background 0.15s, color 0.15s, border-color 0.15s, transform 0.15s; + flex-shrink: 0; + + svg { + width: 18px; + height: 18px; + } + + &:hover { + border-color: var(--brand); + color: var(--brand); + transform: scale(1.05); + } + + &:active { + transform: scale(0.95); + } + } + + .profile{ + a{ + text-decoration: none; + } + + .avatar{ + display: flex; + align-items: center; + justify-content: center; + width: 34px; + height: 34px; + border-radius: 50%; + background-color: var(--brand); + color: white; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: transform 150ms; + + &:hover{ + transform: scale(1.05); + } + + &:active{ + transform: scale(0.96); + } + } + } + + .signin{ + a{ + text-decoration: none; + color: var(--text-secondary); + white-space: nowrap; + + span{ + font-size: 15px; + font-weight: 500; + cursor: pointer; + display: flex; + align-items: center; + gap: 4px; + + img{ + width: 16px; + } + } + } + } + } +} diff --git a/src/components/NavBar/NavBar.tsx b/src/components/NavBar/NavBar.tsx new file mode 100644 index 0000000..95e02ef --- /dev/null +++ b/src/components/NavBar/NavBar.tsx @@ -0,0 +1,202 @@ +"use client"; + +import { useEffect, useState, useRef } from "react"; +import Link from "next/link"; +import { useParams } from "next/navigation"; +import { Toaster } from "react-hot-toast"; +import axios from "axios"; + +import notify from "../../services/toast"; +import { images } from "../../constant"; +import { useTheme } from "../../context/ThemeContext"; +import style from "./NavBar.module.scss"; + +const NavBar = () => { + const params = useParams() as { projectId?: string; roomId?: string }; + const boxRef = useRef(null); + const [username, setUsername] = useState(""); + const [isLoggedIn, setLoggedIn] = useState(false); + const [isBoxVisible, setIsBoxVisible] = useState(false); + const { theme, toggleTheme } = useTheme(); + + const projectId = params?.projectId || params?.roomId; + const shareableLink = + typeof window !== "undefined" && projectId + ? `${window.location.origin}/editor/${projectId}` + : ""; + + const initial = username ? username.charAt(0).toUpperCase() : "?"; + + const toggleBoxVisibility = () => { + setIsBoxVisible(!isBoxVisible); + }; + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (boxRef.current && !boxRef.current.contains(event.target as Node)) { + setIsBoxVisible(false); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [boxRef]); + + const copyLinkHandler = () => { + navigator.clipboard.writeText(shareableLink); + setIsBoxVisible(false); + notify("Link Copied Successfully."); + }; + + useEffect(() => { + const cookies = document.cookie.split("; "); + const tokenCookie = cookies.find((c) => c.startsWith("AuthToken=")); + const userCookie = cookies.find((c) => c.startsWith("username=")); + + if (userCookie) { + setUsername(decodeURIComponent(userCookie.split("=")[1])); + } + + if (tokenCookie) { + const token = tokenCookie.split("=")[1]; + axios + .get("/api/protected/isLoggedIn", { + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, + }) + .then((res) => { + if (res.status === 200) { + setLoggedIn(true); + } + }) + .catch(() => { + setLoggedIn(false); + }); + } + }, []); + + return ( + <> +
+
+ + CodeLab + +
+ {projectId ? ( +
+
+ + + + + Share +
+ + {isBoxVisible && ( +
{ + event.stopPropagation(); + }} + > +

Share this link with other people:

+
+ + + + + + + + Copy link + +
+
+ )} +
+ ) : null} + + + + {isLoggedIn ? ( +
+ +
+ {initial} +
+ +
+ ) : ( +
+ + + Sign in + + +
+ )} +
+
+
+ + + ); +}; + +export default NavBar; diff --git a/Frontend/src/components/SignUp/SignUp.jsx b/src/components/SignUp/SignUp.jsx similarity index 77% rename from Frontend/src/components/SignUp/SignUp.jsx rename to src/components/SignUp/SignUp.jsx index e6d9289..2ba78c4 100644 --- a/Frontend/src/components/SignUp/SignUp.jsx +++ b/src/components/SignUp/SignUp.jsx @@ -1,14 +1,13 @@ -import React from "react"; +"use client"; + import axios from "axios"; -import { useCookies } from "react-cookie"; -import { Link, useNavigate } from "react-router-dom"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; import "./SignUp.scss"; const SignUp = () => { - const [cookies, setCookie] = useCookies(["username"]); - const navigate = useNavigate(); - document.title = "Register"; + const router = useRouter(); const passwordValidater = (password) => { const regex = @@ -25,23 +24,24 @@ const SignUp = () => { if (!passwordValidater(password)) { alert( - "Password must contain at least 8 characters, 1 upp…e, 1 lowercase, 1 number, and 1 special character" + "Password must contain at least 8 characters, 1 uppercase, 1 lowercase, 1 number, and 1 special character" ); - return; } axios - .post("http://localhost:8000/user/register", { + .post("/api/user/register", { username: username, email: email, password: password, }) .then((res) => { - setCookie("email", email, { path: "/" }); - setCookie("username", res.data.username, { path: "/" }); - setCookie("userId", res.data.userId, { path: "/" }); - navigate("/"); + document.cookie = `email=${email}; path=/`; + document.cookie = `username=${res.data.username}; path=/`; + document.cookie = `userId=${res.data.userId}; path=/`; + document.cookie = `AuthToken=${res.data.AuthToken}; path=/`; + + router.push("/dashboard"); }) .catch((error) => { alert(error.response.data.message); @@ -100,7 +100,7 @@ const SignUp = () => {

- Already have an account? Sign in instead + Already have an account? Sign in instead

diff --git a/Frontend/src/components/SignUp/SignUp.scss b/src/components/SignUp/SignUp.scss similarity index 71% rename from Frontend/src/components/SignUp/SignUp.scss rename to src/components/SignUp/SignUp.scss index 712dcf5..b2f0a87 100644 --- a/Frontend/src/components/SignUp/SignUp.scss +++ b/src/components/SignUp/SignUp.scss @@ -4,6 +4,8 @@ align-items: center; width: 100%; height: 100%; + min-height: 100vh; + background: var(--bg-primary); .login{ display: flex; @@ -16,7 +18,7 @@ h1{ font-size: 24px; font-weight: 500; - color: rgba(47, 43, 61, 0.9); + color: var(--text-primary); line-height: 38px; padding-bottom: 5px; } @@ -25,11 +27,11 @@ font-size: 15px; font-weight: 500; line-height: 22px; - color: rgba(47, 43, 61, 0.7); + color: var(--text-secondary); } - + .example{ - background-color: #e8e7fd; + background-color: var(--brand-light); padding: 10px; border-radius: 5px; margin: 20px 0px; @@ -38,7 +40,7 @@ font-size: 13px; font-weight: 500; line-height: 20px; - color: #7367f0; + color: var(--brand); } } @@ -47,10 +49,10 @@ font-size: 13px; font-weight: 500; line-height: 20px; - color: rgba(47, 43, 61, 0.9); + color: var(--text-primary); &::placeholder{ - color: rgba(47, 43, 61, 0.3); + color: var(--text-faint); } } @@ -62,17 +64,18 @@ font-size: 15px; line-height: 24px; font-weight: 500; - color: rgba(47, 43, 61, 0.9); + color: var(--text-primary); + background: var(--bg-input); - border: 1px solid rgba(47, 43, 61, 0.3); + border: 1px solid var(--border-color); border-radius: 5px; &:hover{ - border-color: rgba(47, 43, 61, 0.6); + border-color: var(--border-hover); } &:focus-visible{ - border-color: #7367f0; + border-color: var(--brand); } } @@ -86,7 +89,7 @@ justify-content: center; align-items: center; font-size: 15px; - color: rgba(47, 43, 61, 0.9); + color: var(--text-primary); input{ width: 16px; @@ -96,7 +99,7 @@ } a{ - color: #7367f0; + color: var(--brand); text-decoration: none; font-weight: 500; } @@ -106,7 +109,7 @@ width: 100%; text-align: center; padding: 8px; - background-color: #7367f0; + background: var(--brand); border-radius: 5px; border: 0px; margin: 20px 0px; @@ -114,6 +117,13 @@ font-size: 15px; line-height: 22px; color: #ffffff; + cursor: pointer; + font-weight: 600; + transition: background 0.15s; + + &:hover{ + background: var(--brand-hover); + } } } @@ -123,14 +133,14 @@ font-size: 15px; line-height: 22px; font-weight: 400; - color: rgba(47, 43, 61, 0.9); + color: var(--text-primary); a{ text-decoration: none; font-weight: 500; - color: #7367f0; + color: var(--brand); } } } } -} \ No newline at end of file +} diff --git a/src/components/dashboard/CreateProjectDialog.tsx b/src/components/dashboard/CreateProjectDialog.tsx new file mode 100644 index 0000000..21b9c31 --- /dev/null +++ b/src/components/dashboard/CreateProjectDialog.tsx @@ -0,0 +1,217 @@ +"use client"; + +import { useState } from "react"; + +interface CreateProjectDialogProps { + open: boolean; + onClose: () => void; + onCreate: (data: { + title: string; + description: string; + programmingLanguage: string; + visibility: "public" | "private"; + }) => void; +} + +const LANGUAGES = [ + "javascript", + "typescript", + "python", + "html", + "css", + "json", +]; + +export default function CreateProjectDialog({ + open, + onClose, + onCreate, +}: CreateProjectDialogProps) { + const [title, setTitle] = useState(""); + const [description, setDescription] = useState(""); + const [language, setLanguage] = useState("javascript"); + const [visibility, setVisibility] = useState<"public" | "private">("private"); + + if (!open) return null; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (!title.trim()) return; + onCreate({ title: title.trim(), description: description.trim(), programmingLanguage: language, visibility }); + setTitle(""); + setDescription(""); + setLanguage("javascript"); + setVisibility("private"); + }; + + return ( +
+
e.stopPropagation()}> +
+

Create New Project

+ +
+ +
+
+ + setTitle(e.target.value)} + placeholder="My Awesome Project" + required + autoFocus + /> +
+ +
+ +