Merge user-management-service into chat-service branch#4
Conversation
- Initialize service structure with MVC architecture
- Configure environment variables with .env and .env.example - Add .gitignore for Node.js security - Create service documentation and npm scripts
- Remove .env from version control - Add comprehensive .gitignore for entire project
- Complete User model with bcrypt authentication and security - Production-ready Docker setup with health checks - MongoDB connection with retry logic and error handling - Makefile with development workflow commands - Comprehensive documentation and .dockerignore optimization
No changes required - empty comment with no actionable feedback
There was a problem hiding this comment.
Pull request overview
This pull request attempts to merge the user-management-service into the chat-service branch, bringing together two backend microservices. However, there is a critical naming discrepancy: the PR description states it adds backend/user-management-service/ but the actual directory is backend/user-service/.
Changes:
- Adds a complete user management microservice with MongoDB backend
- Includes Docker configuration for the user service and MongoDB
- Updates frontend package-lock.json to remove peer dependency flags
- Adds root-level Makefile for Docker orchestration and .gitignore for the monorepo
Reviewed changes
Copilot reviewed 11 out of 16 changed files in this pull request and generated 17 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/package-lock.json | Removes "peer": true flags from multiple dependencies, changing dependency resolution behavior |
| docker-compose.yml | Adds Docker services for user-service and MongoDB with health checks and environment configuration |
| backend/user-service/src/server.js | Entry point that connects to database and starts Express server with graceful error handling |
| backend/user-service/src/models/User.js | Comprehensive user model with authentication, 2FA, OAuth, friends system, and security features |
| backend/user-service/src/config/database.js | Database connection manager with retry logic, health checks, and graceful shutdown |
| backend/user-service/src/app.js | Express application setup with middleware (helmet, CORS, morgan, body parsing) and basic routes |
| backend/user-service/package.json | Node.js dependencies including Express 5, Mongoose 9, and security packages |
| backend/user-service/package-lock.json | Full dependency lock file for reproducible builds |
| backend/user-service/dockerfile | Multi-stage Docker build with security hardening and health checks |
| backend/user-service/README.md | Comprehensive documentation covering architecture, setup, and deployment |
| backend/user-service/.gitignore | Standard Node.js gitignore patterns for dependencies, logs, and environment files |
| backend/user-service/.env.example | Template for environment variables including database, JWT, and service configuration |
| Makefile | Docker Compose orchestration commands for building, running, and managing services |
| .gitignore | Root-level gitignore with patterns for environment files, dependencies, and IDE artifacts |
Files not reviewed (2)
- backend/user-service/package-lock.json: Language not supported
- frontend/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1,21 @@ | |||
| FROM node:18-alpine | |||
There was a problem hiding this comment.
The Dockerfile filename uses lowercase "dockerfile" but Docker convention is to use "Dockerfile" with a capital D. While Docker will work with either, following the standard naming convention improves consistency and compatibility with various tools.
| twoFactorBackupCodes: [{ | ||
| code: String, | ||
| used: { type: Boolean, default: false } | ||
| }], |
There was a problem hiding this comment.
The twoFactorBackupCodes field stores backup codes with select: false not set, which means these sensitive hashed codes will be included in query results by default. This is a security risk. The field definition should include select: false to prevent accidental exposure, similar to the password field.
| twoFactorBackupCodes: [{ | |
| code: String, | |
| used: { type: Boolean, default: false } | |
| }], | |
| twoFactorBackupCodes: { | |
| type: [{ | |
| code: String, | |
| used: { type: Boolean, default: false } | |
| }], | |
| select: false | |
| }, |
| app.use(helmet()); | ||
| app.use(cors()); |
There was a problem hiding this comment.
The CORS configuration uses cors() with no options, which allows all origins. This is a security vulnerability in production. The configuration should restrict allowed origins based on environment variables (ALLOWED_ORIGINS from .env.example) to prevent unauthorized cross-origin requests.
| app.use(helmet()); | |
| app.use(cors()); | |
| const allowedOrigins = (process.env.ALLOWED_ORIGINS || '') | |
| .split(',') | |
| .map(origin => origin.trim()) | |
| .filter(origin => origin.length > 0); | |
| const corsOptions = { | |
| origin: (origin, callback) => { | |
| // Allow requests with no origin (e.g. curl, mobile apps) | |
| if (!origin) { | |
| return callback(null, true); | |
| } | |
| if (allowedOrigins.includes(origin)) { | |
| return callback(null, true); | |
| } | |
| return callback(new Error('Not allowed by CORS')); | |
| } | |
| }; | |
| app.use(helmet()); | |
| app.use(cors(corsOptions)); |
|
|
||
| const app = express(); | ||
|
|
||
| app.use(helmet()); |
There was a problem hiding this comment.
The helmet middleware is used with default settings, but for production security, consider configuring specific security policies. At minimum, you should configure Content Security Policy (CSP) headers to prevent XSS attacks and other injection vulnerabilities.
| app.use(helmet()); | |
| app.use( | |
| helmet({ | |
| contentSecurityPolicy: { | |
| useDefaults: true, | |
| directives: { | |
| defaultSrc: ["'self'"] | |
| } | |
| } | |
| }) | |
| ); |
| setTimeout(() => { | ||
| this.connect(); | ||
| }, this.retryDelay); | ||
|
|
||
| // Exponential backoff | ||
| this.retryDelay *= 1.5; |
There was a problem hiding this comment.
The retry logic uses setTimeout (line 82-84) which doesn't work well in a async/await context. The function will continue execution immediately after setTimeout is called, not after the delay. This should use a Promise-based delay or await a promisified setTimeout to ensure the retry happens after the delay period.
| setTimeout(() => { | |
| this.connect(); | |
| }, this.retryDelay); | |
| // Exponential backoff | |
| this.retryDelay *= 1.5; | |
| // Capture current delay, then apply exponential backoff for the next attempt | |
| const currentDelay = this.retryDelay; | |
| // Exponential backoff | |
| this.retryDelay *= 1.5; | |
| // Wait for the delay period before retrying the connection | |
| await new Promise((resolve) => setTimeout(resolve, currentDelay)); | |
| await this.connect(); |
| app.use((error, req, res, next) => { | ||
| console.error('Error: ', error); | ||
| res.status(500).json({ | ||
| message: 'Internal server error', | ||
| error: process.env.NODE_ENV === 'development' ? error.message : undefined | ||
| }); |
There was a problem hiding this comment.
The error handler middleware reveals error details in development mode (line 29), which is acceptable. However, there's no request ID or correlation ID logging, making it difficult to trace errors in production. Consider adding request IDs for better observability.
| @@ -0,0 +1,21 @@ | |||
| FROM node:18-alpine | |||
There was a problem hiding this comment.
The package.json specifies engine requirements including "node": ">=20.19.0" for bson and mongoose dependencies. However, the Dockerfile uses FROM node:18-alpine (line 1), which is incompatible with these dependencies. This will cause installation failures. Update the Dockerfile to use Node.js 20 or higher.
| FROM node:18-alpine | |
| FROM node:20-alpine |
| this.isConnected = true; | ||
| this.retryCount = 0; | ||
|
|
||
| console.log(`MongoDB connected successfully to: ${mongoUri.replace(/\/\/.*@/, '//***:***@')}`); |
There was a problem hiding this comment.
The database connection string masking on line 37 uses a simple regex replacement that may not work correctly for all MongoDB URI formats. The pattern /\/\/.*@/ will fail for URIs without authentication credentials, potentially causing a runtime error. Use optional matching or check if credentials exist before attempting to mask them.
| console.log(`MongoDB connected successfully to: ${mongoUri.replace(/\/\/.*@/, '//***:***@')}`); | |
| const safeMongoUri = mongoUri && mongoUri.includes('@') | |
| ? mongoUri.replace(/^(mongodb(?:\+srv)?:\/\/)[^@]+@/, '$1***:***@') | |
| : mongoUri; | |
| console.log(`MongoDB connected successfully to: ${safeMongoUri}`); |
|
|
||
| ## Prerequisites | ||
|
|
||
| - Node.js (v18 or higher) |
There was a problem hiding this comment.
The README states the prerequisite is "Node.js (v18 or higher)" on line 29, but the package.json dependencies (mongoose, bson, mongodb) require Node.js >= 20.19.0. This is a documentation inconsistency that could mislead developers. Update the README to reflect the actual minimum version requirement of Node.js 20.19.0.
| - Node.js (v18 or higher) | |
| - Node.js (v20.19.0 or higher) |
| "express": "^5.2.1", | ||
| "helmet": "^8.1.0", | ||
| "jsonwebtoken": "^9.0.3", | ||
| "mongoose": "^9.1.5", |
There was a problem hiding this comment.
The mongoose version "^9.1.5" requires Node.js >= 20.19.0 according to the package-lock.json (line 1009). This is a very specific and recent Node.js version requirement that may not be widely available yet. Mongoose 8.x supports older Node.js versions and would be more compatible. Consider whether this version requirement is necessary or if an earlier mongoose version would be more practical.
Changes
backend/user-management-service/directory from thefeature/user-management-databasebranchExpected Backend Structure After Merge