Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions task-management-backend/src/config/redisClient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const { createClient } = require('redis');
const { fromEnv } = require('../utils');


const REDIS_CONFIG = {
url: fromEnv('REDIS_URL') || 'redis://localhost:6379',
socket: {
reconnectStrategy: (retries) => {
logger.warn(`Redis connection retry attempt: ${retries}`);
return Math.min(retries * 100, 5000);
}
}
};


const redisClient = createClient(REDIS_CONFIG);


module.exports = redisClient;
24 changes: 24 additions & 0 deletions task-management-backend/src/controllers/authController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const {responseManager, authManager} = require('../services')

const register = async(request,response) => {
try{
const result = await authManager.register(request.body)
return responseManager.sendSuccessResponse(response,result,'User Created Successfully!')
}catch(err){
return responseManager.sendErrorResponse(response,err,'Cannot Create User')
}
}

const login = async(request,response) => {
try{
const result = await authManager.login(request.body)
return responseManager.sendSuccessResponse(response,result,'User login successfull')
}catch(err){
return responseManager.sendErrorResponse(response,err,'Error login user')
}
}




module.exports = {register,login}
4 changes: 2 additions & 2 deletions task-management-backend/src/controllers/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const taskController = require('./taskController')
const userController = require('./userController')

module.exports = {taskController,userController}
const authController = require('./authController')
module.exports = {taskController,userController,authController}
26 changes: 10 additions & 16 deletions task-management-backend/src/controllers/userController.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
const {responseManager, userManager} = require('../services')
const {redisClient} = require('../server')

const register = async(request,response) => {
const getUser = async(request,response) => {
try{
const result = await userManager.register(request.body)
return responseManager.sendSuccessResponse(response,result,'User Created Successfully!')
const result = await userManager.getUser()
return responseManager.sendSuccessResponse(response,result,'User details fetched successfully!')
}catch(err){
return responseManager.sendErrorResponse(response,err,'Cannot Create User')
return responseManager.sendErrorResponse(response,err,'Cannot get all user')
}
}

const login = async(request,response) => {
const assignUser = async(request,response) => {

try{
const result = await login({
...request.body,
ip: request.ip,
userAgent: request.headers['user-agent']
}, redisClient); // Pass redisClient here
return responseManager.sendSuccessResponse(response,result,'User Logged In Successfully!')
const result = await userManager.assignUser(request.body)
return responseManager.sendSuccessResponse(response,result,'User Assigned Successfully')
}catch(err){
return responseManager.sendErrorResponse(response,err,'Cannot login User')
return responseManager.sendErrorResponse(response,err,'Cannot assign user')
}
}


module.exports = {register,login}
module.exports = {getUser,assignUser}
33 changes: 33 additions & 0 deletions task-management-backend/src/middleware/verifyToken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const { AppError } = require("../utils");
const { fromEnv } = require("../utils");
const jwt = require("jsonwebtoken");
const { userModel } = require("../models");
const { NO_AUTH_HEADER, INVALID_ACCESS_TOKEN, NOT_FOUND } = require("../utils/errors");
const _ = require("lodash");
const verifyToken = async (req, res, next) => {
try {
const { authorization } = req.headers;
if (_.isEmpty(authorization)) {
const error = NO_AUTH_HEADER;
throw new AppError(error.code, error.message, error.statusCode);
}

const accessToken = authorization.split(" ")[1];
if (!accessToken) {
const error = INVALID_ACCESS_TOKEN;
throw new AppError(error.code, error.message, error.statusCode);
}
const decodedToken = jwt.verify(accessToken, fromEnv('SECRET_KEY'));
const user = await userModel.findOne({ email: decodedToken.email });
if (_.isEmpty(user)) {
const error = NOT_FOUND;
throw new AppError(error.code, error.message, error.statusCode);
}
req.user = user;
next();
} catch (err) {
next(err);
}
};

module.exports = verifyToken
4 changes: 4 additions & 0 deletions task-management-backend/src/models/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ const taskSchema = new Schema({
enum: ['completed', 'in-progress', 'not-started'],
default: 'not-started'
},
assignTo:{
type:mongoose.Schema.Types.ObjectId,
ref:'User'
}
}, {
timestamps: true
});
Expand Down
9 changes: 9 additions & 0 deletions task-management-backend/src/routes/authRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const express = require('express');
const { userSchema } = require('../validation');
const { authController } = require('../controllers');
const {verifyData} = require('../middleware');
const router = express.Router();

router.post('/register',verifyData(userSchema),authController.register)
router.post('/login',verifyData(userSchema),authController.login)
module.exports = router
3 changes: 2 additions & 1 deletion task-management-backend/src/routes/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
const express = require('express');
const taskRoutes = require('./taskRoutes');
const userRoutes = require('./userRoutes');
const authRoutes = require('./authRoutes');

const router = express.Router();
console.log("Task and User routes mounted");

router.use('/tasks',taskRoutes)
router.use('/user',userRoutes)
router.use('/auth',authRoutes)

module.exports = router;
8 changes: 4 additions & 4 deletions task-management-backend/src/routes/userRoutes.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const express = require('express');
const { userSchema } = require('../validation');
const verifyToken = require('../middleware/verifyToken');
const { userController } = require('../controllers');
const {verifyData} = require('../middleware');
const router = express.Router();

router.post('/register',verifyData(userSchema),userController.register)
router.post('/login',verifyData(userSchema),userController.login)

router.get('/',verifyToken,userController.getUser)
router.post('/assign',verifyToken,userController.assignUser)

module.exports = router
190 changes: 18 additions & 172 deletions task-management-backend/src/server.js
Original file line number Diff line number Diff line change
@@ -1,181 +1,27 @@
const express = require('express');
const { fromEnv } = require('./utils');
const { createClient } = require('redis');
const { logger } = require('./utils');
const connectDB = require('./config');
const connectDB = require('./config/connection');
const routes = require('./routes');
const cors = require('cors');
// const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const app = express();

// Configuration constants
const SERVER_PORT = fromEnv('PORT') || 3002;
const REDIS_CONFIG = {
url: fromEnv('REDIS_URL') || 'redis://localhost:6379',
socket: {
reconnectStrategy: (retries) => {
logger.warn(`Redis connection retry attempt: ${retries}`);
return Math.min(retries * 100, 5000);
}
}
};


// Enhanced CORS configuration
const corsOptions = {
origin: ['http://localhost:3000'], // Array of allowed origins
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'Accept'],
credentials: true,
optionsSuccessStatus: 200
};

// Critical middleware - ordered correctly
// app.use(helmet()); // Security headers first
app.use(cors(corsOptions)); // CORS before other middleware

app.use(express.json({ limit: '10kb' })); // Body parsing
app.use(express.urlencoded({ extended: true })); // URL-encoded data

// Rate limiting configuration
const apiLimiter = rateLimit({
windowMs: 45 * 60 * 1000,
max: 100,
handler: (req, res) => {
logger.warn(`Rate limit exceeded for IP: ${req.ip}`);
res.status(429).json({
error: 'Too many requests, please try again later'
});
}
});

const redisClient = createClient(REDIS_CONFIG);

async function startServer() {
// Redis connection
redisClient.on('error', err => logger.error('Redis Client Error:', err));
redisClient.on('ready', () => logger.info('Redis client ready'));
redisClient.on('reconnecting', () => logger.warn('Redis client reconnecting'));

try {
await redisClient.connect();
logger.info('✅ Connected to Redis successfully');
} catch (err) {
logger.error('❌ Failed to connect to Redis:', err);
process.exit(1);
}

// Database connection
try {
await connectDB();
logger.info('✅ Database connected successfully');
} catch (err) {
logger.error('❌ Database connection failed:', err);
process.exit(1);
}

// Apply rate limiting to API routes
app.use('/api', apiLimiter);

// Attach Redis client to app context
app.locals.redis = redisClient;

// Routes - moved after all middleware but before error handlers
app.use('/api',routes);

// Root endpoint
app.get('/', (req, res) => {
res.status(200).json({
status: 'UP',
timestamp: new Date().toISOString(),
redis: redisClient.isReady ? 'connected' : 'disconnected',
environment: process.env.NODE_ENV || 'development'
});
const PORT = fromEnv('PORT') || 3002;
connectDB();

// Middleware
app.use(express.json());
app.use(cors());

app.use('/api', routes);

app.get('/', (req, res) => {
res.status(200).json({
status: 'UP',
timestamp: new Date().toISOString()
});

// Health check endpoint
app.get('/health', async (req, res) => {
try {
await redisClient.set('healthcheck', 'ok', { EX: 10 });
const redisValue = await redisClient.get('healthcheck');

res.status(200).json({
status: 'healthy',
components: {
database: true,
redis: redisValue === 'ok',
memory: process.memoryUsage(),
uptime: process.uptime()
},
timestamp: new Date().toISOString()
});
} catch (err) {
res.status(503).json({
status: 'unhealthy',
error: err.message,
timestamp: new Date().toISOString()
});
}
});

// Not found handler (after all other routes)
app.use((req, res) => {
res.status(404).json({ error: 'Not found' });
});

// Error handler (must be last middleware)
app.use((err, req, res, next) => {
logger.error('Server error:', err);
res.status(500).json({
error: 'Internal server error',
message: process.env.NODE_ENV === 'development' ? err.message : undefined
});
});

const server = app.listen(SERVER_PORT, () => {
logger.info(`🚀 Server running in ${process.env.NODE_ENV || 'development'} mode on port ${SERVER_PORT}`);
});

// Graceful shutdown
const shutdown = async (signal) => {
logger.info(`Received ${signal}. Shutting down gracefully...`);

try {
await redisClient.quit();
logger.info('Redis connection closed');
} catch (err) {
logger.error('Error closing Redis connection:', err);
}

server.close(() => {
logger.info('HTTP server closed');
process.exit(0);
});

setTimeout(() => {
logger.error('Forcing shutdown after timeout');
process.exit(1);
}, 10000);
};

process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));

process.on('unhandledRejection', (err) => {
logger.error('Unhandled rejection:', err);
shutdown('unhandledRejection');
});

process.on('uncaughtException', (err) => {
logger.error('Uncaught exception:', err);
shutdown('uncaughtException');
});
}

startServer().catch(err => {
logger.error('Fatal error during server startup:', err);
process.exit(1);
});

module.exports = { app, redisClient };

app.listen(PORT, () => {
logger.info(`🚀 Server running at PORT: ${PORT}`);
});
Loading