From 42f5ade6ce83a8ef61a043f111d464a70423aed4 Mon Sep 17 00:00:00 2001 From: kenf1 <103538771+kenf1@users.noreply.github.com> Date: Sun, 17 Aug 2025 16:30:24 +0000 Subject: [PATCH 1/2] Update npm packages + format with prettier --- .dockerignore | 3 +- .gitignore | 1 + apps/backend/ecosystem.config.js | 23 ++++----- apps/backend/package-lock.json | 60 ++++++++++-------------- apps/backend/src/config/db.ts | 20 ++++---- apps/backend/src/index.ts | 51 ++++++++++---------- apps/backend/src/models/media.model.ts | 2 +- apps/backend/src/models/message.model.ts | 3 +- apps/backend/src/models/room.model.ts | 4 +- apps/backend/src/models/user.model.ts | 2 +- 10 files changed, 84 insertions(+), 85 deletions(-) diff --git a/.dockerignore b/.dockerignore index d905a38..b23c00b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,5 @@ docs/ README.md node_modules -npm-debug.log \ No newline at end of file +npm-debug.log +.devcontainer \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1c9270c..2756fd0 100644 --- a/.gitignore +++ b/.gitignore @@ -154,3 +154,4 @@ vite.config.ts.timestamp-* # Misc .DS_Store *.pem +.devcontainer/ \ No newline at end of file diff --git a/apps/backend/ecosystem.config.js b/apps/backend/ecosystem.config.js index 7c7c851..da55799 100644 --- a/apps/backend/ecosystem.config.js +++ b/apps/backend/ecosystem.config.js @@ -7,35 +7,35 @@ module.exports = { exec_mode: 'cluster', env: { NODE_ENV: 'development', - PORT: 3000 + PORT: 3000, }, env_production: { NODE_ENV: 'production', - PORT: 3000 + PORT: 3000, }, // Logging log_file: './logs/combined.log', out_file: './logs/out.log', error_file: './logs/error.log', log_date_format: 'YYYY-MM-DD HH:mm:ss Z', - + // Auto restart configuration max_memory_restart: '1G', restart_delay: 4000, max_restarts: 10, min_uptime: '10s', - + // Health monitoring kill_timeout: 5000, listen_timeout: 3000, - + // Source map support for better error tracking source_map_support: true, - + // Auto restart when file changes (disable in production) watch: false, - ignore_watch: ['node_modules', 'logs'] - } + ignore_watch: ['node_modules', 'logs'], + }, ], deploy: { @@ -45,7 +45,8 @@ module.exports = { ref: 'origin/main', repo: 'git@github.com:username/shared-media-streaming.git', path: '/var/www/production', - 'post-deploy': 'pnpm install && pnpm run build:prod && pm2 reload ecosystem.config.js --env production' - } - } + 'post-deploy': + 'pnpm install && pnpm run build:prod && pm2 reload ecosystem.config.js --env production', + }, + }, }; diff --git a/apps/backend/package-lock.json b/apps/backend/package-lock.json index 878d22a..c800573 100644 --- a/apps/backend/package-lock.json +++ b/apps/backend/package-lock.json @@ -14,6 +14,7 @@ "cors": "^2.8.5", "dotenv": "^17.2.1", "express": "^4.19.2", + "express-mongo-sanitize": "^2.2.0", "mongoose": "^8.17.0", "multer": "^2.0.2" }, @@ -51,9 +52,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, @@ -456,9 +457,9 @@ } }, "node_modules/@types/node": { - "version": "24.2.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz", - "integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==", + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", "license": "MIT", "dependencies": { "undici-types": "~7.10.0" @@ -1436,6 +1437,15 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-mongo-sanitize": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/express-mongo-sanitize/-/express-mongo-sanitize-2.2.0.tgz", + "integrity": "sha512-PZBs5nwhD6ek9ZuP+W2xmpvcrHwXZxD5GdieX2dsjPbAbH4azOkrHbycBud2QRU+YQF1CT+pki/lZGedHgo/dQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/extrareqp2": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/extrareqp2/-/extrareqp2-1.0.0.tgz", @@ -1879,26 +1889,15 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", "devOptional": true, "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, "engines": { "node": ">= 12" } }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "devOptional": true, - "license": "BSD-3-Clause" - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -2090,13 +2089,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "devOptional": true, - "license": "MIT" - }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -2321,9 +2313,9 @@ } }, "node_modules/mongoose": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.17.0.tgz", - "integrity": "sha512-mxW6TBPHViORfNYOFXCVOnT4d5aRr+CgDxTs1ViYXfuHzNpkelgJQrQa+Lz6hofoEQISnKlXv1L3ZnHyJRkhfA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.17.1.tgz", + "integrity": "sha512-aodS4cacux5caoxB5ErEwRmrafIUsVRJxHnvP7URnSUnTenr32j1qBVV+KjYxryyLSisQkxglAFF69TNLeZTLg==", "license": "MIT", "dependencies": { "bson": "^6.10.4", @@ -3486,13 +3478,13 @@ } }, "node_modules/socks": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", - "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==", + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "devOptional": true, "license": "MIT", "dependencies": { - "ip-address": "^9.0.5", + "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" }, "engines": { diff --git a/apps/backend/src/config/db.ts b/apps/backend/src/config/db.ts index 07077e1..1788c7f 100644 --- a/apps/backend/src/config/db.ts +++ b/apps/backend/src/config/db.ts @@ -1,22 +1,24 @@ -import mongoose from "mongoose"; +import mongoose from 'mongoose'; export const connectDB = async () => { - console.log("🔍 Debug - Environment variables:"); - console.log("MONGO_URI:", process.env.MONGO_URI); - console.log("PORT:", process.env.PORT); - + console.log('🔍 Debug - Environment variables:'); + console.log('MONGO_URI:', process.env.MONGO_URI); + console.log('PORT:', process.env.PORT); + if (!process.env.MONGO_URI) { - console.error("❌ MONGO_URI is not defined in environment variables"); - console.error("💡 Make sure your .env file is in the correct location and contains MONGO_URI"); + console.error('❌ MONGO_URI is not defined in environment variables'); + console.error( + '💡 Make sure your .env file is in the correct location and contains MONGO_URI', + ); process.exit(1); } try { console.log(`🔄 Connecting to MongoDB...`); await mongoose.connect(process.env.MONGO_URI as string); - console.log("✅ MongoDB connected successfully"); + console.log('✅ MongoDB connected successfully'); } catch (err) { - console.error("❌ MongoDB connection error:", err); + console.error('❌ MongoDB connection error:', err); process.exit(1); } }; diff --git a/apps/backend/src/index.ts b/apps/backend/src/index.ts index f13c7d9..4c3d7cf 100644 --- a/apps/backend/src/index.ts +++ b/apps/backend/src/index.ts @@ -1,40 +1,43 @@ -import fs from "node:fs"; -import path from "node:path"; -import dotenv from "dotenv"; - -import app from "./app"; -import { connectDB } from "./config/db"; +import fs from 'node:fs'; +import path from 'node:path'; +import dotenv from 'dotenv'; +import app from './app'; +import { connectDB } from './config/db'; // Load environment variables based on NODE_ENV -const nodeEnv = process.env.NODE_ENV || "development"; -const envFiles = [`.env.${nodeEnv}.local`, `.env.${nodeEnv}`, ".env.local", ".env"]; +const nodeEnv = process.env.NODE_ENV || 'development'; +const envFiles = [ + `.env.${nodeEnv}.local`, + `.env.${nodeEnv}`, + '.env.local', + '.env', +]; console.log(`🌍 Environment: ${nodeEnv}`); // Load the first existing environment file for (const envFile of envFiles) { - const envPath = path.resolve(process.cwd(), envFile); - if (fs.existsSync(envPath)) { - dotenv.config({ path: envPath }); - console.log(`🔧 Loading environment from: ${envPath}`); - break; - } + const envPath = path.resolve(process.cwd(), envFile); + if (fs.existsSync(envPath)) { + dotenv.config({ path: envPath }); + console.log(`🔧 Loading environment from: ${envPath}`); + break; + } } - const port = process.env.PORT || 3000; async function startServer() { - try { - await connectDB(); - app.listen(port, () => { - console.log(`application running on port ${port}`); - }); - } catch (error) { - console.error("failed to start the server", error); - process.exit(1); - } + try { + await connectDB(); + app.listen(port, () => { + console.log(`application running on port ${port}`); + }); + } catch (error) { + console.error('failed to start the server', error); + process.exit(1); + } } startServer(); diff --git a/apps/backend/src/models/media.model.ts b/apps/backend/src/models/media.model.ts index b98cded..f2787c3 100644 --- a/apps/backend/src/models/media.model.ts +++ b/apps/backend/src/models/media.model.ts @@ -7,7 +7,7 @@ const mediaSchema = new mongoose.Schema({ size: { type: Number }, duration: { type: Number }, uploadedBy: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, - createdAt: { type: Date, default: Date.now } + createdAt: { type: Date, default: Date.now }, }); export default mongoose.model('Media', mediaSchema); diff --git a/apps/backend/src/models/message.model.ts b/apps/backend/src/models/message.model.ts index 7074b2e..083735c 100644 --- a/apps/backend/src/models/message.model.ts +++ b/apps/backend/src/models/message.model.ts @@ -1,4 +1,3 @@ - import mongoose from 'mongoose'; const messageSchema = new mongoose.Schema({ @@ -6,7 +5,7 @@ const messageSchema = new mongoose.Schema({ senderId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, senderName: { type: String }, content: { type: String, required: true }, - createdAt: { type: Date, default: Date.now } + createdAt: { type: Date, default: Date.now }, }); export default mongoose.model('Message', messageSchema); diff --git a/apps/backend/src/models/room.model.ts b/apps/backend/src/models/room.model.ts index a93b6bd..c3125d9 100644 --- a/apps/backend/src/models/room.model.ts +++ b/apps/backend/src/models/room.model.ts @@ -7,10 +7,10 @@ const roomSchema = new mongoose.Schema({ playback: { state: { type: String, enum: ['playing', 'paused'], default: 'paused' }, currentTime: { type: Number, default: 0 }, - updatedAt: { type: Date, default: Date.now } + updatedAt: { type: Date, default: Date.now }, }, createdAt: { type: Date, default: Date.now }, - expiresAt: { type: Date } + expiresAt: { type: Date }, }); export default mongoose.model('Room', roomSchema); diff --git a/apps/backend/src/models/user.model.ts b/apps/backend/src/models/user.model.ts index d760818..9159d41 100644 --- a/apps/backend/src/models/user.model.ts +++ b/apps/backend/src/models/user.model.ts @@ -6,7 +6,7 @@ const userSchema = new mongoose.Schema({ password: { type: String }, avatarUrl: { type: String }, createdAt: { type: Date, default: Date.now }, - lastActiveAt: { type: Date } + lastActiveAt: { type: Date }, }); export default mongoose.model('User', userSchema); From 6ba7156a72a681be4babdf1bf62bda3eee305fba Mon Sep 17 00:00:00 2001 From: kenf1 <103538771+kenf1@users.noreply.github.com> Date: Sun, 17 Aug 2025 16:49:04 +0000 Subject: [PATCH 2/2] Migrate src/index.js --- apps/backend/src/app.ts | 58 ++++++++++++ apps/backend/src/config/db.ts | 10 +- apps/backend/src/middleware/errorHandler.ts | 8 +- package.json | 4 +- src/index.js | 100 -------------------- 5 files changed, 69 insertions(+), 111 deletions(-) delete mode 100644 src/index.js diff --git a/apps/backend/src/app.ts b/apps/backend/src/app.ts index 308e720..26363d0 100644 --- a/apps/backend/src/app.ts +++ b/apps/backend/src/app.ts @@ -5,6 +5,7 @@ import mongoSanitize from 'express-mongo-sanitize'; import globalErrorHandler from './middleware/errorHandler'; import AppError from './utils/appError'; import { HTTP_STATUS } from './constants/httpStatus'; +import AWS from 'aws-sdk'; const app = express(); @@ -176,6 +177,63 @@ app.use('*', (_req, res) => { }); }); +app.post('/upload-test-file', async (_req, res) => { + const bucketName = process.env.S3_BUCKET; + const fileName = `test-file-${Date.now()}.txt`; + const fileContent = 'This is a test file uploaded from my Express app!'; + + AWS.config.update({ + accessKeyId: process.env.S3_USER_KEY, + secretAccessKey: process.env.S3_SECRET, + region: process.env.S3_REGION, + }); + + const s3 = new AWS.S3(); + + if (!bucketName) { + return res.status(500).json({ + success: false, + message: 'S3_BUCKET environment variable is not set.', + }); + } + + const params = { + Bucket: bucketName, + Key: fileName, + Body: fileContent, + ContentType: 'text/plain', + ACL: 'private', + }; + + try { + const data = await s3.upload(params).promise(); + res.json({ + success: true, + message: 'Test file uploaded successfully to S3!', + fileLocation: data.Location, + fileName: data.Key, + bucket: data.Bucket, + }); + } catch (error: unknown) { + if (error instanceof Error) { + console.error('S3 upload failed:', error.message); + res.status(500).json({ + success: false, + message: 'Failed to upload test file to S3.', + error: error.message, + }); + } else { + // fallback for non-Error types + console.error('S3 upload failed with unknown error:', error); + res.status(500).json({ + success: false, + message: 'Failed to upload test file to S3.', + error: String(error), + }); + } + } +}); + // Global error handler app.use(globalErrorHandler); diff --git a/apps/backend/src/config/db.ts b/apps/backend/src/config/db.ts index 1788c7f..57e3898 100644 --- a/apps/backend/src/config/db.ts +++ b/apps/backend/src/config/db.ts @@ -1,12 +1,12 @@ import mongoose from 'mongoose'; export const connectDB = async () => { - console.log('🔍 Debug - Environment variables:'); + console.log('Debug - Environment variables:'); console.log('MONGO_URI:', process.env.MONGO_URI); console.log('PORT:', process.env.PORT); if (!process.env.MONGO_URI) { - console.error('❌ MONGO_URI is not defined in environment variables'); + console.error('MONGO_URI is not defined in environment variables'); console.error( '💡 Make sure your .env file is in the correct location and contains MONGO_URI', ); @@ -14,11 +14,11 @@ export const connectDB = async () => { } try { - console.log(`🔄 Connecting to MongoDB...`); + console.log(`Connecting to MongoDB...`); await mongoose.connect(process.env.MONGO_URI as string); - console.log('✅ MongoDB connected successfully'); + console.log('MongoDB connected successfully'); } catch (err) { - console.error('❌ MongoDB connection error:', err); + console.error('MongoDB connection error:', err); process.exit(1); } }; diff --git a/apps/backend/src/middleware/errorHandler.ts b/apps/backend/src/middleware/errorHandler.ts index c6d9e0d..883ca67 100644 --- a/apps/backend/src/middleware/errorHandler.ts +++ b/apps/backend/src/middleware/errorHandler.ts @@ -3,12 +3,12 @@ import AppError from '../utils/appError'; import mongoose from 'mongoose'; import { HTTP_STATUS } from '../constants/httpStatus'; -const handleCastErroDB = (err: mongoose.Error.CastError) => { +const handleCastErrorDB = (err: mongoose.Error.CastError) => { const message = `Invalid ${err.path}: ${err.value}.`; return new AppError(message, HTTP_STATUS.BAD_REQUEST); }; -const handleDublicateFieldsDB = (err: any) => { +const handleDuplicateFieldsDB = (err: any) => { const value = err.errorResponse.errmsg.match(/(["'])(\\?.)*?\1/)[0]; const message = `The value '${value}' is already in use. Please choose a different value.`; @@ -64,8 +64,8 @@ const globalErrorHandler = ( } else if (process.env.NODE_ENV === 'production') { let error = Object.create(err); - if (error.name === 'CastError') error = handleCastErroDB(error); - if (error.code === 11000) error = handleDublicateFieldsDB(error); + if (error.name === 'CastError') error = handleCastErrorDB(error); + if (error.code === 11000) error = handleDuplicateFieldsDB(error); if (error.name === 'ValidationError') error = handleValidationErrorDB(error); diff --git a/package.json b/package.json index d9d3936..9a0103d 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,10 @@ "start:prod": "NODE_ENV=production node apps/backend/dist/index.js", "lint": "turbo run lint", "format": "prettier --write \"**/*.{ts,tsx,md}\"", - "fmt": "npx prettier . --write", "check-types": "turbo run check-types", "clean": "rm -rf node_modules", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "fmtbe":"npx prettier --write apps/backend" }, "repository": { "type": "git", diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 248d819..0000000 --- a/src/index.js +++ /dev/null @@ -1,100 +0,0 @@ -const express = require("express"); -const { MongoClient } = require("mongodb"); -const AWS = require("aws-sdk"); - -const app = express(); -const port = 3000; - -const mongoUri = - process.env.MONGO_PRIVATE_URL || - process.env.MONGO_URL || - `mongodb://${process.env.MONGOUSER || process.env.MONGO_INITDB_ROOT_USERNAME}:${process.env.MONGO_INITDB_ROOT_PASSWORD}@${process.env.MONGOHOST}:${process.env.MONGOPORT}`; - -app.get("/", (req, res) => { - res.send("

Welcome to the Shared Media Streaming Service

"); -}); - -app.get("/check-mongo", async (req, res) => { - let client; - - try { - client = new MongoClient(mongoUri, { - useNewUrlParser: true, - useUnifiedTopology: true, - }); - await client.connect(); - - const adminDb = client.db().admin(); - const serverStatus = await adminDb.serverStatus(); - - res.json({ - success: true, - message: "Successfully connected to MongoDB.", - host: serverStatus.host, - version: serverStatus.version, - uptime: serverStatus.uptime, - }); - } catch (error) { - console.error("MongoDB connection failed:", error); - res.status(500).json({ - success: false, - message: "Failed to connect to MongoDB.", - error: error.message, - }); - } finally { - if (client) { - await client.close(); - } - } -}); - -app.post("/upload-test-file", async (req, res) => { - const bucketName = process.env.S3_BUCKET; - const fileName = `test-file-${Date.now()}.txt`; - const fileContent = "This is a test file uploaded from my Express app!"; - - AWS.config.update({ - accessKeyId: process.env.S3_USER_KEY, - secretAccessKey: process.env.S3_SECRET, - region: process.env.S3_REGION, - }); - - const s3 = new AWS.S3(); - - if (!bucketName) { - return res.status(500).json({ - success: false, - message: "S3_BUCKET environment variable is not set.", - }); - } - - const params = { - Bucket: bucketName, - Key: fileName, - Body: fileContent, - ContentType: "text/plain", - ACL: "private", - }; - - try { - const data = await s3.upload(params).promise(); - res.json({ - success: true, - message: "Test file uploaded successfully to S3!", - fileLocation: data.Location, - fileName: data.Key, - bucket: data.Bucket, - }); - } catch (error) { - console.error("S3 upload failed:", error); - res.status(500).json({ - success: false, - message: "Failed to upload test file to S3.", - error: error.message, - }); - } -}); - -app.listen(port, () => { - console.log(`Example app listening on port ${port}`); -});