From 2a88aae302e4243e227eac8835c62f59981f31ae Mon Sep 17 00:00:00 2001 From: roeeHaim Date: Sun, 31 Aug 2025 14:13:23 +0300 Subject: [PATCH] mongo set up --- docker-compose.yml | 15 +++ web_server/.env | 7 +- web_server/.env.example | 4 + web_server/app.js | 24 +++- web_server/config/db.js | 51 +++++++++ web_server/package-lock.json | 208 ++++++++++++++++++++++++++++++++++- web_server/package.json | 4 +- 7 files changed, 306 insertions(+), 7 deletions(-) create mode 100644 web_server/.env.example create mode 100644 web_server/config/db.js diff --git a/docker-compose.yml b/docker-compose.yml index 450fd7ac..8ee57660 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,15 @@ services: + # MongoDB database + mongo: + profiles: ["web_app"] + image: mongo:6 + container_name: mongo + volumes: + - mongo_data:/data/db + ports: + - "27017:27017" + networks: + - gmail-net # React frontend web_front: profiles: ["web_app"] @@ -26,6 +37,9 @@ services: - ./web_server/.env depends_on: - run_server + - mongo + environment: + - MONGO_URI=mongodb://mongo:27017/appdb ports: - "${NODE_PORT:-3001}:3001" networks: @@ -91,6 +105,7 @@ services: volumes: app_data: + mongo_data: networks: gmail-net: \ No newline at end of file diff --git a/web_server/.env b/web_server/.env index a344b204..de934bda 100644 --- a/web_server/.env +++ b/web_server/.env @@ -1,5 +1,4 @@ -REACT_URL=http://localhost:3000 NODE_PORT=3001 -JSON_LIMIT=10mb -JWT_SECRET=gradingMode -JWT_EXPIRATION_TIME=24h \ No newline at end of file +JWT_SECRET=replace_me +JWT_EXPIRATION_TIME=24h +MONGO_URI=mongodb://localhost:27017/appdb diff --git a/web_server/.env.example b/web_server/.env.example new file mode 100644 index 00000000..91e29f52 --- /dev/null +++ b/web_server/.env.example @@ -0,0 +1,4 @@ +NODE_PORT=3001 +JWT_SECRET=replace_me +JWT_EXPIRATION_TIME=24h +MONGO_URI=mongodb://mongo:27017/appdb diff --git a/web_server/app.js b/web_server/app.js index eb7b5c1c..4348f8c0 100644 --- a/web_server/app.js +++ b/web_server/app.js @@ -17,10 +17,32 @@ const inbox = require('./routes/mails'); const users = require('./routes/users'); const labels = require('./routes/labels'); const blacklist = require('./routes/blacklist'); +const { connectDB } = require('./config/db'); +const health = require('./routes/health'); +// Dev routes are disabled by default. Uncomment if needed for local debugging. +// const devUsers = require('./routes/devUsers'); +// const devLabels = require('./routes/devLabels'); +// const devMails = require('./routes/devMails'); app.use('/api/mails', inbox); app.use('/api', users); app.use('/api/labels', labels); app.use('/api/blacklist', blacklist); +app.use('/health', health); +// app.use('/dev/users', devUsers); +// app.use('/dev/labels', devLabels); +// app.use('/dev/mails', devMails); -app.listen(PORT); +const start = async () => { + try { + await connectDB(); + app.listen(PORT, () => { + console.log(`Server listening on port ${PORT}`); + }); + } catch (err) { + console.error('Failed to start server:', err && err.message ? err.message : err); + process.exit(1); + } +}; + +start(); diff --git a/web_server/config/db.js b/web_server/config/db.js new file mode 100644 index 00000000..d2147a2a --- /dev/null +++ b/web_server/config/db.js @@ -0,0 +1,51 @@ +const mongoose = require('mongoose'); + +mongoose.set('strictQuery', true); + +const MAX_RETRIES = 3; +const RETRY_DELAYS_MS = [1000, 2000, 4000]; + +function addConnectionListeners() { + const connection = mongoose.connection; + connection.on('connected', () => { + console.log('Mongo connected'); + }); + connection.on('error', (err) => { + console.error('Mongo connection error:', err && err.message ? err.message : err); + }); + connection.on('disconnected', () => { + console.warn('Mongo disconnected'); + }); +} + +async function connectDB() { + addConnectionListeners(); + + const mongoUri = process.env.MONGO_URI; + + if (!mongoUri) { + throw new Error('MONGO_URI is not defined in environment variables'); + } + + for (let attemptIndex = 0; attemptIndex < MAX_RETRIES; attemptIndex += 1) { + try { + await mongoose.connect(mongoUri); + return mongoose.connection; + } catch (error) { + const isLastAttempt = attemptIndex === MAX_RETRIES - 1; + if (isLastAttempt) { + throw error; + } + const delay = RETRY_DELAYS_MS[attemptIndex] || 1000; + console.warn(`Mongo connection attempt ${attemptIndex + 1} failed. Retrying in ${delay}ms...`); + await new Promise((resolve) => setTimeout(resolve, delay)); + } + } + + // Should never reach here + throw new Error('Unknown Mongo connection error'); +} + +module.exports = { connectDB }; + + diff --git a/web_server/package-lock.json b/web_server/package-lock.json index 0ad1fdb6..5e41f6a4 100644 --- a/web_server/package-lock.json +++ b/web_server/package-lock.json @@ -12,7 +12,32 @@ "cors": "^2.8.5", "dotenv": "^17.2.0", "express": "^5.1.0", - "jsonwebtoken": "^9.0.2" + "jsonwebtoken": "^9.0.2", + "mongoose": "^8.6.1" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz", + "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" } }, "node_modules/accepts": { @@ -48,6 +73,15 @@ "node": ">=18" } }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "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", @@ -516,6 +550,15 @@ "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==", + "license": "Apache-2.0", + "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", @@ -576,6 +619,12 @@ "node": ">= 0.8" } }, + "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==", + "license": "MIT" + }, "node_modules/merge-descriptors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", @@ -609,6 +658,105 @@ "node": ">= 0.6" } }, + "node_modules/mongodb": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.18.0.tgz", + "integrity": "sha512-fO5ttN9VC8P0F5fqtQmclAkgXZxbIkYRTUi1j8JO6IYwvamkhtYDilJr35jOPELR49zqCJgXZWwCtW7B+TM8vQ==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.4", + "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 || ^2.0.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.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.18.0.tgz", + "integrity": "sha512-3TixPihQKBdyaYDeJqRjzgb86KbilEH07JmzV8SoSjgoskNTpa6oTBmDxeoF9p8YnWQoz7shnCyPkSV/48y3yw==", + "license": "MIT", + "dependencies": { + "bson": "^6.10.4", + "kareem": "2.6.3", + "mongodb": "~6.18.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/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -697,6 +845,15 @@ "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==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", @@ -905,6 +1062,21 @@ "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -923,6 +1095,18 @@ "node": ">=0.6" } }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -955,6 +1139,28 @@ "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==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/web_server/package.json b/web_server/package.json index 5090cf97..4c267797 100644 --- a/web_server/package.json +++ b/web_server/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "main": "app.js", "scripts": { + "dev": "node app.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], @@ -14,6 +15,7 @@ "cors": "^2.8.5", "dotenv": "^17.2.0", "express": "^5.1.0", - "jsonwebtoken": "^9.0.2" + "jsonwebtoken": "^9.0.2", + "mongoose": "^8.6.1" } }