diff --git a/package-lock.json b/package-lock.json index ac28f19..c6263b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "dotenv": "^16.4.4", "express": "5.0.0", "jsonwebtoken": "^9.0.2", - "mongodb": "^6.3.0", + "mongoose": "^8.8.2", "rimraf": "^5.0.5", "typescript": "^5.3.3", "ws": "8.17.1" @@ -45,9 +45,9 @@ } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.4.tgz", - "integrity": "sha512-8zJ8N1x51xo9hwPh6AWnKdLGEC5N3lDa6kms1YHmFBoRhTpJR6HG8wWk0td1MVCu9cD4YBrvjZEtd5Obw0Fbnw==", + "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" } @@ -320,9 +320,9 @@ } }, "node_modules/bson": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.3.0.tgz", - "integrity": "sha512-balJfqwwTBddxfnidJZagCBPP/f48zj9Sdp3OJswREOgsJzHiQSaOIAtApSgDQFYgHqAvFkp53AFSqjMDZoTFw==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.0.tgz", + "integrity": "sha512-ROchNosXMJD2cbQGm84KoP7vOGPO6/bOAW0veMMbzhXLqoZptcaYRVLitwvuhwhjjpU1qP4YZRWLhgETdgqUQw==", "engines": { "node": ">=16.20.1" } @@ -963,6 +963,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/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -1087,12 +1095,12 @@ } }, "node_modules/mongodb": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", - "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.10.0.tgz", + "integrity": "sha512-gP9vduuYWb9ZkDM546M+MP2qKVk5ZG2wPF63OvSRuUbqCR+11ZCAE1mOfllhlAG0wcoJY5yDL/rV3OmYEwXIzg==", "dependencies": { - "@mongodb-js/saslprep": "^1.1.0", - "bson": "^6.2.0", + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", "mongodb-connection-string-url": "^3.0.0" }, "engines": { @@ -1140,6 +1148,51 @@ "whatwg-url": "^13.0.0" } }, + "node_modules/mongoose": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.2.tgz", + "integrity": "sha512-jCTSqDANfRzk909v4YoZQi7jlGRB2MTvgG+spVBc/BA4tOs1oWJr//V6yYujqNq9UybpOtsSfBqxI0dSOEFJHQ==", + "dependencies": { + "bson": "^6.7.0", + "kareem": "2.6.3", + "mongodb": "~6.10.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/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1540,6 +1593,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/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -1931,9 +1989,9 @@ } }, "@mongodb-js/saslprep": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.4.tgz", - "integrity": "sha512-8zJ8N1x51xo9hwPh6AWnKdLGEC5N3lDa6kms1YHmFBoRhTpJR6HG8wWk0td1MVCu9cD4YBrvjZEtd5Obw0Fbnw==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", "requires": { "sparse-bitfield": "^3.0.3" } @@ -2165,9 +2223,9 @@ } }, "bson": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.3.0.tgz", - "integrity": "sha512-balJfqwwTBddxfnidJZagCBPP/f48zj9Sdp3OJswREOgsJzHiQSaOIAtApSgDQFYgHqAvFkp53AFSqjMDZoTFw==" + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.0.tgz", + "integrity": "sha512-ROchNosXMJD2cbQGm84KoP7vOGPO6/bOAW0veMMbzhXLqoZptcaYRVLitwvuhwhjjpU1qP4YZRWLhgETdgqUQw==" }, "buffer-equal-constant-time": { "version": "1.0.1", @@ -2609,6 +2667,11 @@ "safe-buffer": "^5.0.1" } }, + "kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==" + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -2699,12 +2762,12 @@ "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==" }, "mongodb": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", - "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.10.0.tgz", + "integrity": "sha512-gP9vduuYWb9ZkDM546M+MP2qKVk5ZG2wPF63OvSRuUbqCR+11ZCAE1mOfllhlAG0wcoJY5yDL/rV3OmYEwXIzg==", "requires": { - "@mongodb-js/saslprep": "^1.1.0", - "bson": "^6.2.0", + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", "mongodb-connection-string-url": "^3.0.0" } }, @@ -2717,6 +2780,40 @@ "whatwg-url": "^13.0.0" } }, + "mongoose": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.2.tgz", + "integrity": "sha512-jCTSqDANfRzk909v4YoZQi7jlGRB2MTvgG+spVBc/BA4tOs1oWJr//V6yYujqNq9UybpOtsSfBqxI0dSOEFJHQ==", + "requires": { + "bson": "^6.7.0", + "kareem": "2.6.3", + "mongodb": "~6.10.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==" + }, + "mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "requires": { + "debug": "4.x" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -2975,6 +3072,11 @@ "object-inspect": "^1.13.1" } }, + "sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" + }, "signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", diff --git a/package.json b/package.json index d584955..e708bd0 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "dotenv": "^16.4.4", "express": "5.0.0", "jsonwebtoken": "^9.0.2", - "mongodb": "^6.3.0", + "mongoose": "^8.8.2", "rimraf": "^5.0.5", "typescript": "^5.3.3", "ws": "8.17.1" diff --git a/src/db/models/user.ts b/src/db/models/user.ts index d0250f8..3bf02bb 100644 --- a/src/db/models/user.ts +++ b/src/db/models/user.ts @@ -1,18 +1,46 @@ -import { ObjectId } from "mongodb"; +import "dotenv/config"; -export default class User { - constructor( - public name: string, - public uuid: string, - public id: ObjectId, - public config : UserConfig - ) {} +import {ObjectId} from "mongodb"; +import mongoose from "mongoose"; + + +export interface IUser { + name: string, + uuid: string, + id: ObjectId, + config: UserConfig } -export class UserConfig { - constructor( - public isVisible: boolean , - public canBeModified: boolean, - public isAdmin: boolean - ) {} +export interface UserConfig { + isVisible: boolean, + canBeModified: boolean, + isAdmin: boolean } + +const userSchema = new mongoose.Schema({ + name: { + type: String, + required: true, + }, + uuid: { + type: String, + required: true, + }, + config: { + isVisible: { + type: Boolean, + required: true, + }, + canBeModified: { + type: Boolean, + required: true, + }, + isAdmin: { + type: Boolean, + required: true, + }, + }, +}); + + +export const UserModel = mongoose.model(process.env.USER_COLLECTION_NAME!, userSchema); diff --git a/src/db/services/UserService.ts b/src/db/services/UserService.ts new file mode 100644 index 0000000..e311ca0 --- /dev/null +++ b/src/db/services/UserService.ts @@ -0,0 +1,29 @@ +import { UserModel, IUser } from "../models/user"; +import {connectToDatabase} from "./database.service"; +import mongoose from "mongoose"; + +export class UserService { + private static _instance: UserService; + + private constructor() {} + + public static async create(): Promise { + if (!this._instance) { + await connectToDatabase(); + this._instance = new UserService(); + } + return this._instance; + } + + public async updateUser(id: string, user: Partial): Promise { + return await UserModel.findByIdAndUpdate(id, user, { new: true }).exec(); + } + + public async getAllUsers(): Promise { + return await UserModel.find().exec(); + } + + public async getUserById(id: string): Promise { + return await UserModel.findById(id).exec(); + } +} diff --git a/src/db/services/database.service.ts b/src/db/services/database.service.ts index 65da213..ae1bdb7 100644 --- a/src/db/services/database.service.ts +++ b/src/db/services/database.service.ts @@ -1,68 +1,12 @@ import "dotenv/config"; -import * as mongoDB from "mongodb"; -import {ObjectId, ReturnDocument} from "mongodb"; -import User from "../models/user"; +import mongoose from "mongoose"; -let mongoDb: mongoDB.Db; +export async function connectToDatabase() { + await mongoose.connect(process.env.DB_CONN_STRING!, { + dbName: process.env.DB_NAME!, + serverApi: {version: '1', strict: true, deprecationErrors: true} + }); -async function getDatabase(): Promise { - if (mongoDb) { - return mongoDb; - } - const client = new mongoDB.MongoClient(process.env.DB_CONN_STRING!); - await client.connect(); - mongoDb = client.db(process.env.DB_NAME!); - return mongoDb; -} - -export class UserService { - private static _instance: UserService; - private readonly collection: mongoDB.Collection; - - private constructor(db: mongoDB.Db) { - this.collection = db.collection(process.env.USER_COLLECTION_NAME!); - } - - public static async create(): Promise { - if (this._instance) { - return this._instance; - } - const db = await getDatabase(); - this._instance = new UserService(db); - return this._instance; - } - - public async updateUser(id: string, user: User): Promise { - return await this.executeWithExceptionHandling(() => { - return this.collection.findOneAndUpdate( - {_id: new ObjectId(id)}, - {$set: user}, - {returnDocument: ReturnDocument.AFTER} - ); - }) as unknown as User; - } - - public async getAllUsers() { - return await this.executeWithExceptionHandling(() => { - return this.collection.find({}).toArray(); - }); - } - - async getUserById(id: string) { - return (await this.executeWithExceptionHandling(() => { - return this.collection.findOne({ - _id: new ObjectId(id), - }); - })); - } - - private async executeWithExceptionHandling(operation: () => Promise) { - try { - return await operation(); - } catch (e) { - console.error(e); - return null; - } - } + console.log("Connected to MongoDB with Mongoose"); } diff --git a/src/index.ts b/src/index.ts index 58b2e39..b4a7c67 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,6 @@ import express from "express"; import { ExtendedWebSocketServer } from "./websocket"; import { RestWebSocket } from "./rest/restWebSocket"; -import { UserService } from "./db/services/database.service"; import { RestUser } from "./rest/restUser"; import { authenticateJwt } from "./rest/middleware/authenticateJwt"; import { JwtTokenPropertiesExtractor } from "./rest/jwtTokenPropertiesExtractor"; @@ -24,7 +23,7 @@ app.use(express.json({ limit: "15mb" })); const webSocketServer = new ExtendedWebSocketServer(server); const restWebSocket = new RestWebSocket(webSocketServer); -const restUser = new RestUser(UserService.create); +const restUser = new RestUser(); const jwtTokenPropertiesExtractor = new JwtTokenPropertiesExtractor(); app.use("/api/websocket", authenticateJwt, restWebSocket.createRouter()); diff --git a/src/rest/restUser.ts b/src/rest/restUser.ts index be9c10f..e215aaa 100644 --- a/src/rest/restUser.ts +++ b/src/rest/restUser.ts @@ -1,21 +1,19 @@ -import { UserService } from "../db/services/database.service"; import express from "express"; -import User from "../db/models/user"; +import {UserService} from "../db/services/UserService"; +import {IUser} from "../db/models/user"; export class RestUser { - constructor(private createService: () => Promise) {} - public createRouter() { const router = express.Router(); router.get("/", async (req, res) => { - const userService = await this.createService(); + const userService = await UserService.create(); const users = await userService.getAllUsers(); res.status(200).send({ users }); }); router.get("/:id", async (req, res) => { - const userService = await this.createService(); + const userService = await UserService.create(); const id = req.params.id; const user = await userService.getUserById(id); @@ -27,9 +25,9 @@ export class RestUser { }); router.put("/:id", async (req, res) => { - const userService = await this.createService(); + const userService = await UserService.create(); const id = req.params.id; - const user = req.body as User; + const user = req.body as IUser; const result = await userService.updateUser(id, user); result