From f17ed17365f1b27d45c262c68b0fb4a57718a108 Mon Sep 17 00:00:00 2001 From: jayllo-c Date: Sun, 6 Oct 2024 12:08:34 +0800 Subject: [PATCH] Basic implemntation of session service Additions: - session interface for mongoose to store session information - session routes to make use of http requests and responses - code for creation of session, joining, and session deletion - implemented jwt validation into joining and deletion requests --- .vscode/settings.json | 2 + collaboration-service/package-lock.json | 216 ++++++++++++++++++ collaboration-service/package.json | 2 + .../src/controller/session-controller.ts | 63 +++++ collaboration-service/src/index.ts | 14 -- .../src/middleware/jwt-validation.ts | 2 +- .../src/model/session-model.ts | 25 ++ .../src/routes/session-routes.ts | 20 ++ .../src/routes/socket-routes.ts | 6 - collaboration-service/src/server.ts | 17 +- .../editor-handler.ts} | 3 +- 11 files changed, 344 insertions(+), 26 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 collaboration-service/src/controller/session-controller.ts delete mode 100644 collaboration-service/src/index.ts create mode 100644 collaboration-service/src/model/session-model.ts create mode 100644 collaboration-service/src/routes/session-routes.ts delete mode 100644 collaboration-service/src/routes/socket-routes.ts rename collaboration-service/src/{controller/socket-controller.ts => utils/editor-handler.ts} (98%) diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..7a73a41bfd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/collaboration-service/package-lock.json b/collaboration-service/package-lock.json index 748d1088e9..9316e88895 100644 --- a/collaboration-service/package-lock.json +++ b/collaboration-service/package-lock.json @@ -14,6 +14,8 @@ "jsonwebtoken": "^9.0.2", "mode": "^0.3.2", "model": "^0.0.6", + "mongodb": "^6.9.0", + "mongoose": "^8.7.0", "redis-adapter": "^0.1.0", "socket.io": "^4.7.5", "utils": "^0.3.1" @@ -68,6 +70,14 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@redis/bloom": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", @@ -283,6 +293,19 @@ "@types/send": "*" } }, + "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.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -553,6 +576,14 @@ "node": ">=8" } }, + "node_modules/bson": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", + "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==", + "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", @@ -1287,6 +1318,14 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/kind-of": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", @@ -1386,6 +1425,11 @@ "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.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -1468,6 +1512,126 @@ "node": "*" } }, + "node_modules/mongodb": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.9.0.tgz", + "integrity": "sha512-UMopBVx1LmEUbW/QE0Hw18u583PEDVQmUmVzzBRH0o/xtE9DBRA5ZYLOjpLIa03i8FXjzvQECJcqoMvCXftTUA==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.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.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.7.0.tgz", + "integrity": "sha512-rUCSF1mMYQXjXYdqEQLLlMD3xbcj2j1/hRn+9VnVj7ipzru/UoUZxlj/hWmteKMAh4EFnDZ+BIrmma9l/0Hi1g==", + "dependencies": { + "bson": "^6.7.0", + "kareem": "2.6.3", + "mongodb": "6.9.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "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.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mquery/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/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1700,6 +1864,14 @@ "dev": true, "license": "MIT" }, + "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.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -1917,6 +2089,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" + }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -2031,6 +2208,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "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", @@ -2088,6 +2273,17 @@ "nodetouch": "bin/nodetouch.js" } }, + "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/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -2262,6 +2458,26 @@ "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/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/collaboration-service/package.json b/collaboration-service/package.json index 11676a7626..dd4e77e9fa 100644 --- a/collaboration-service/package.json +++ b/collaboration-service/package.json @@ -18,6 +18,8 @@ "jsonwebtoken": "^9.0.2", "mode": "^0.3.2", "model": "^0.0.6", + "mongodb": "^6.9.0", + "mongoose": "^8.7.0", "redis-adapter": "^0.1.0", "socket.io": "^4.7.5", "utils": "^0.3.1" diff --git a/collaboration-service/src/controller/session-controller.ts b/collaboration-service/src/controller/session-controller.ts new file mode 100644 index 0000000000..7fb74a280d --- /dev/null +++ b/collaboration-service/src/controller/session-controller.ts @@ -0,0 +1,63 @@ +import { Request, Response } from 'express'; +import Session from '../model/session-model'; + +export const sessionController = { + createSession: async (req: Request, res: Response) => { + const { + session_id, // assuming session id is passed in the request body + participants, + question, + code, + } = req.body; + + // const sessionId = Math.random().toString(36).substring(2, 8); + const session = new Session({ + session_id: session_id, //sessionId, + date_created: new Date(), + participants, + question, + code, + }); + + await session.save() + .then((data) => { + res.status(201).json(data); + }) + .catch((err) => { + res.status(500).json({ message: (err as Error).message }); + }); + }, + joinSession: async (req: Request, res: Response) => { + const { sessionId, userId } = req.body + + try { + // Find a session that the user is a participant of + const session = await Session.findOne({ participants: userId }); + + if (!session) { + return res.status(404).json({ message: 'Session not found' }); + } + + res.status(200).json(session); + } catch (err) { + res.status(500).json({ message: (err as Error).message }); + } + }, + terminateSession: async (req: Request, res: Response) => { + const { sessionId } = req.body; + + try { + const session = await Session.findOne({ session_id: sessionId }); + + if (!session) { + return res.status(404).json({ message: 'Session not found' }); + } + + await Session.deleteOne({ session_id: sessionId }); + + res.status(200).json({ message: 'Session terminated successfully' }); + } catch (err) { + res.status(500).json({ message: (err as Error).message }); + } + } +}; \ No newline at end of file diff --git a/collaboration-service/src/index.ts b/collaboration-service/src/index.ts deleted file mode 100644 index a8683f6c79..0000000000 --- a/collaboration-service/src/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import express, { Application } from 'express'; -import router from './routes/template-routes'; -import authMiddleware from './middleware/template-middleware'; - -const app: Application = express(); - -// Middleware -app.use(express.json()); -app.use(authMiddleware); - -// Routes -app.use('/api', router); - -export default app; diff --git a/collaboration-service/src/middleware/jwt-validation.ts b/collaboration-service/src/middleware/jwt-validation.ts index fd9c853b1c..5daaacbd42 100644 --- a/collaboration-service/src/middleware/jwt-validation.ts +++ b/collaboration-service/src/middleware/jwt-validation.ts @@ -44,4 +44,4 @@ export function validateSocketJWT(socket: Socket, next: (err?: Error) => void) { export function validateJWT (token: string): JwtPayload { return jwt.verify(token, secretKey) as JwtPayload; -} \ No newline at end of file +} diff --git a/collaboration-service/src/model/session-model.ts b/collaboration-service/src/model/session-model.ts new file mode 100644 index 0000000000..1cdb2cf63a --- /dev/null +++ b/collaboration-service/src/model/session-model.ts @@ -0,0 +1,25 @@ +import mongoose, { Schema } from "mongoose"; + +interface Session { + session_id: number; + date_created: Date; + participants: string[]; + question: string; + code: string; +} + +const sessionSchema: Schema = new Schema({ + session_id: { type: Number, unique: true }, + date_created: { type: Date, required: true }, + participants: { + type: [String], + required: true, + validate: { + validator: (v: string[]) => v.length == 2}, + message: "A session must have exactly 2 participants.", + }, + question: { type: String, required: true }, + code: { type: String, required: true }, +}); + +export default mongoose.model("Session", sessionSchema); \ No newline at end of file diff --git a/collaboration-service/src/routes/session-routes.ts b/collaboration-service/src/routes/session-routes.ts new file mode 100644 index 0000000000..a99ccd9e8e --- /dev/null +++ b/collaboration-service/src/routes/session-routes.ts @@ -0,0 +1,20 @@ +import { Request, Response, Router } from "express"; +import { validateApiJWT } from '../middleware/jwt-validation'; +import { sessionController } from "../controller/session-controller"; + +const router = Router(); + +// Test route +router.get('/', (req, res) => {res.send('Hello from session service!')}); + +// Create a new session +router.post("/session/create", sessionController.createSession); + +// Join a session +router.post("/session/join", validateApiJWT, sessionController.joinSession); + +// Terminate a session +router.delete("/session/terminate", validateApiJWT, sessionController.terminateSession); + + +export default router; \ No newline at end of file diff --git a/collaboration-service/src/routes/socket-routes.ts b/collaboration-service/src/routes/socket-routes.ts deleted file mode 100644 index b4423a8110..0000000000 --- a/collaboration-service/src/routes/socket-routes.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Server } from "socket.io"; -import { handleEditorChanges } from "../controller/socket-controller"; - -export function handleEdits(io: Server) { - handleEditorChanges(io); -} \ No newline at end of file diff --git a/collaboration-service/src/server.ts b/collaboration-service/src/server.ts index 0c907c7a9f..94228a3a40 100644 --- a/collaboration-service/src/server.ts +++ b/collaboration-service/src/server.ts @@ -1,26 +1,35 @@ import express from 'express'; import http from 'http'; import { Server } from 'socket.io'; -import { validateSocketJWT } from './middleware/jwt-validation'; -import { handleEdits } from './routes/socket-routes'; +import mongoose from 'mongoose'; import dotenv from 'dotenv'; import cors from 'cors'; +import { validateSocketJWT } from './middleware/jwt-validation'; +import { handleEditorChanges } from './utils/editor-handler'; +import router from './routes/session-routes'; const app = express(); app.use(cors()); +app.use(express.json()); +app.use("/api/session", router) + +mongoose + .connect(process.env.MONGODB_URI as string, {}) + .then(() => console.log('Connected to MongoDB')) + .catch((err) => console.log('Error connecting to MongoDB', err)); const server = http.createServer(app); const io = new Server(server, { cors: { origin: '*', - methods: ['GET', 'POST'], + methods: ['GET', 'PUT', 'POST', 'DELETE'], credentials: true } }); //io.use(validateSocketJWT); -handleEdits(io); +handleEditorChanges(io); export { server }; diff --git a/collaboration-service/src/controller/socket-controller.ts b/collaboration-service/src/utils/editor-handler.ts similarity index 98% rename from collaboration-service/src/controller/socket-controller.ts rename to collaboration-service/src/utils/editor-handler.ts index 536046fc4e..60cbe5a12c 100644 --- a/collaboration-service/src/controller/socket-controller.ts +++ b/collaboration-service/src/utils/editor-handler.ts @@ -20,4 +20,5 @@ export function handleEditorChanges(io: Server) { console.log('user disconnected'); }); }); -}; + +}; \ No newline at end of file