Skip to main content

Command Palette

Search for a command to run...

Mongodb-Practice-1

Updated
17 min readView as Markdown
G

I am Learner.

PORT=3000
SECRET=SECRET
MONGO_URI="mongodb://127.0.0.1:27017/mongodb_practice"
//REFRESH_SECRET=refresh
//ACCESS_SECRET=access

const express = require('express');
require('dotenv').config();
const path = require("path");

const connectDb = require('./utils/db');
const adminSeeder = require("./seeder/adminSeeder");

const authRoutes = require("./router/auth.routes");
const userRoutes = require("./router/user.routes");
const taskRoutes = require("./router/task.routes");

const app = express();

app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));

app.use(express.json());
app.use("/uploads", express.static("uploads"));
app.use("/auth", authRoutes);
app.use("/user", userRoutes);
app.use("/task", taskRoutes);

connectDb().then(async () => {
    await adminSeeder();
    app.listen(process.env.PORT, () => {
        console.log(` Server is running on http://localhost:${process.env.PORT}`);
    });
});

const mongoose = require('mongoose');

const connectDb = async () => {
    try {
        await mongoose.connect(process.env.MONGO_URI);
        console.log("MongoDB connected successfully");
    } catch (error) {
        console.error(" Error connecting to MongoDB:", error);
        process.exit(1); // Exit process if DB fails
    }
};

module.exports = connectDb;



const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
    userName: {
        type: String,
        default: ""
    },
    email: { 
         type: String, 
         default: "",
         unique: true
    },
    password: {
        type: String,
        default: ""
    },
    role: {
        type: String,
        enum: ['User', 'Admin'],
        default: 'User'
    },
    birthDate: {
        type: Date
    },
    age: {
        type: Number
    },
    profileImage: {
        type: String,
        default: ""
    },
    sessions: [{ token: String, createdAt: Date }]
}, {
    timestamps: true
});

const User = mongoose.model('User', userSchema);

module.exports = User;

const mongoose = require('mongoose');

const assignSchema = new mongoose.Schema({
    userId: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    },
    status: {
        type: String,
        enum: ['Pending', 'Ongoing', 'Completed']
    }
});

const commentsSchema = new mongoose.Schema({
    userId: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    },
    media: {
        type: String,
        default: " "
    },
    comment: {
        type: String,
        default: ""
    }
})

const taskSchema = new mongoose.Schema({
    taskName: {
        type: String,
        default: ""
    },
    description: {
        type: String,
        default: ""
    },
    isDeleted: {
        type: Boolean,
        default: false
    },
    createdBy: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    },
    comments: [commentsSchema],
    assignTask: [assignSchema]
}, {
    timestamps: true
});

const Task = mongoose.model('Task', taskSchema);

module.exports = Task;

const mongoose = require("mongoose");

const interestSchema = new mongoose.Schema({
    name: {
        type: [String],
        required: true
    },
    userId: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "User",
        required: true
    }
}, { timestamps: true });

module.exports = mongoose.model("Interest", interestSchema);

const User = require("../model/user.model.js");
const bcrypt = require("bcrypt");

const adminSeeder = async (req, res) => {
    try {
        const email = 'a@a.com';
        const password = '123456';

        const isExistingUser = await User.findOne({ email });
        if (isExistingUser) {
            console.log("Admin already exists.");
            if (res) return res.status(200).json({ message: "Admin already exists" });
            return;
        }
        const hashedPassword = await bcrypt.hash(password, 10);
        const adminUser = new User({
            userName: 'Admin',
            email,
            password: hashedPassword,
            role: 'Admin',
            sessions: []
        });

        await adminUser.save();

        console.log("Admin created successfully:", adminUser);

        if (res) {
            return res.status(201).json({
                message: "Admin created successfully",
                admin: {
                    userName: adminUser.userName,
                    email: adminUser.email,
                    role: adminUser.role
                }
            });
        }

    } catch (error) {
        console.error(" Error creating admin:", error);
        if (res) return res.status(500).json({ message: "Error creating admin", error: error.message });
    }
};

module.exports = adminSeeder;

const multer = require("multer");
const path = require("path");

const storage = multer.diskStorage({
    destination: (req, file, cb) => cb(null, 'uploads/'),
    filename: (req, file, cb) => cb(null, Date.now() + path.extname(file.originalname)),
});

const fileFilter = (req, file, cb) => {
    const allowedTypes = /jpeg|jpg|png|gif|webp/;
    const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
    const mimetype = allowedTypes.test(file.mimetype);

    if (mimetype && extname) {
        cb(null, true);
    } else {
        cb(new Error('Only image files are allowed!'));
    }
};

const upload = multer({
    storage,
    fileFilter,
    limits: { fileSize: 5 * 1024 * 1024 },
});

module.exports = upload;

const express = require('express');
const router = express.Router();
const validate = require("../middleware/validate.js");
const authController = require("../controller/auth.controller.js");
const { authMiddleware } = require("../middleware/authMiddleware.js");
const { registerValidator, loginValidator } = require('../middleware/validationError.js');
const upload = require("../utils/upload.js");


router.post("/Register", upload.single("profileImage"), registerValidator, validate, authController.Register);
router.post("/login", loginValidator, validate, authController.Login);
router.post("/logout", authMiddleware, authController.Logout);
//router.post("/refreshToken", authController.refreshAccessToken);

module.exports = router;

const express = require('express');
const router = express.Router();
const upload = require("../utils/upload.js");
const userController = require("../controller/user.controller.js");
const validate = require("../middleware/validate.js");
const {
    updateUserValidator,
    getUserByIdValidator,
    deleteUserValidator,
    updateStatusValidator,
    commentTaskValidator
} = require("../middleware/validationError.js");
const { isAdmin, authMiddleware } = require("../middleware/authMiddleware.js");

router.get("/getAllUser", authMiddleware, isAdmin, userController.getAllUser);
router.get("/getUser/:id", authMiddleware, isAdmin, getUserByIdValidator, validate, userController.getUserById);
router.delete("/deleteUser/:id", authMiddleware, isAdmin, deleteUserValidator, validate, userController.deleteUserById);
router.put("/updateUser/:id", upload.single("profileImage"), authMiddleware, isAdmin, updateUserValidator, validate, userController.updateUserById);
router.patch("/updateTaskStatus/:taskId", authMiddleware, updateStatusValidator, validate, userController.updateStatus);
router.get("/getAllUserTask", authMiddleware, userController.getAllUserTask);
router.put("/commentTask/:taskId", authMiddleware, upload.single('media'), commentTaskValidator, validate, userController.commentTask);

module.exports = router;

const express = require('express');
const router = express.Router();

const taskController = require("../controller/task.controller.js");
const { isAdmin, authMiddleware } = require("../middleware/authMiddleware.js");
const validate = require("../middleware/validate.js");

const {
    createTaskValidator,
    updateTaskValidator,
    taskIdValidator,
    assignTaskValidator,
    getTaskQueryValidator
} = require("../middleware/validationError.js");

router.post("/createTask", authMiddleware, isAdmin, createTaskValidator, validate, taskController.createTask);

router.get("/getAllTask", authMiddleware, isAdmin, getTaskQueryValidator, validate, taskController.getAllTask);

router.get("/getTask/:id", authMiddleware, isAdmin, taskIdValidator, validate, taskController.getTaskById);

router.delete("/deleteTask/:id", authMiddleware, isAdmin, taskIdValidator, validate, taskController.deleteTaskById);

router.put("/updateTask/:id", authMiddleware, isAdmin, updateTaskValidator, validate, taskController.updateTaskById);


router.put("/softDelete/:id", authMiddleware, isAdmin, taskIdValidator, validate, taskController.softDeleteTask);

router.put("/assignTask/:id", authMiddleware, isAdmin, assignTaskValidator, validate, taskController.assignTask);

module.exports = router;

const User = require("../model/user.model.js");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const Interest = require("../model/interest.model.js");
require('dotenv').config();

// comment when generate access token use
const generateToken = (user) => {
    let expiresIn = "1d";
    if (user.role === "Admin") expiresIn = "7d";
    else if (user.role === "User") expiresIn = "1d";

    return jwt.sign(
        { id: user._id, role: user.role },
        process.env.SECRET,
        { expiresIn }
    );
};

// const generateAccessToken = (user) => {
//     let expiresIn = "10h";
//     return jwt.sign(
//         { id: user._id, role: user.role },
//         process.env.ACCESS_SECRET,
//         { expiresIn }
//     );
// };

// const generateRefreshToken = (user) => {
//     let expiresIn = "7d";
//     if (user.role === "Admin") {
//         expiresIn = "30d";
//     } else if (user.role === 'User') {
//         expiresIn = "7d"
//     }
//     return jwt.sign(
//         { id: user._id, role: user.role },
//         process.env.REFRESH_SECRET,
//         { expiresIn }
//     );
// };

const Register = async (req, res) => {
    try {
        const { userName, email, password, role, birthDate, interests } = req.body;
        if (role === "Admin") {
            const existingAdmin = await User.findOne({ role: "Admin" });
            if (existingAdmin) {
                return res.status(400).json({ message: "Admin already exists" });
            }
        }

        const isExistingUser = await User.findOne({ email });
        if (isExistingUser) {
            return res.status(400).json({ message: "User already exists" });
        }

        const hashedPassword = await bcrypt.hash(password, 10);
        let age = null;
        if (birthDate && !isNaN(new Date(birthDate).getTime())) {
            const dob = new Date(birthDate);
            const diff = Date.now() - dob.getTime();
            age = new Date(diff).getUTCFullYear() - 1970;
        }
        const profileImage = req.file ? `/uploads/${req.file.filename}` : "";

        const user = new User({
            userName,
            email,
            password: hashedPassword,
            role,
            birthDate,
            age,
            profileImage,
            sessions: []
        });

        await user.save();

        let interestsArray = [];
        if (typeof interests === "string") {
            interestsArray = interests.split(",").map(i => i.trim());
        } else if (Array.isArray(interests)) {
            interestsArray = interests;
        }
        if (interestsArray.length > 0) {
            await Interest.create({
                userId: user._id,
                name: interestsArray
            });
        }

        res.status(201).json({
            message: "User registered successfully",
            user: {
                id: user._id,
                userName: user.userName,
                email: user.email,
                role: user.role,
                age: user.age,
                profileImage: user.profileImage,
                interests: interestsArray
            }
        });

    } catch (error) {
        console.error("Registration error:", error);
        res.status(500).json({ message: "Error while registering", error: error.message });
    }
};

const Login = async (req, res) => {
    try {
        const { email, password } = req.body;
        const user = await User.findOne({ email });

        if (!user) {
            return res.status(400).json({ message: "User does not exist." });
        }

        const matchPassword = await bcrypt.compare(password, user.password);
        if (!matchPassword) {
            return res.status(400).json({ message: "Invalid Email or Password." });
        }

        if (user.sessions && user.sessions.length >= 5) {
            return res.status(400).json({ message: "Max 5 sessions allowed. Logout from another device." });
        }
        // comment when generate access token use
        const token = generateToken(user);
        user.sessions.push({ token, createdAt: new Date() });
        await user.save();

        res.status(200).json({ message: "Login successful", token });

        // const accessToken = generateAccessToken(user);
        // const refreshToken = generateRefreshToken(user);
        // user.sessions.push({ token: refreshToken, createdAt: new Date() });
        //await user.save();
        //res.status(200).json({ message: "Login successful", accessToken, refreshToken });
    } catch (error) {
        console.error("Login error:", error);
        res.status(500).json({ message: "Login failed", error: error.message });
    }
};

const Logout = async (req, res) => {
    try { 
         // comment when generate access token use
        if (!req.user || !req.token) {
            return res.status(401).json({ message: "Unauthorized" });
        }
         req.user.sessions = req.user.sessions.filter(s => s.token !== req.token);
         // const { refreshToken } = req.body;
        // if (!refreshToken) {
        //     return res.status(400).json({ message: "Refresh token required" });
        // }
        // req.user.sessions = req.user.sessions.filter(s => s.token !== refreshToken);

        await req.user.save();

        res.status(200).json({ message: "User logged out successfully" });
    } catch (err) {
        res.status(500).json({ message: "Logout failed", error: err.message });
    }
};
// const refreshAccessToken = async (req, res) => {
//     try {
//         const { refreshToken } = req.body;
//         if (!refreshToken) {
//             return res.status(401).json({ message: "Refresh token required" });
//         }

//         const jwt = require("jsonwebtoken");
//         const decoded = jwt.verify(refreshToken, process.env.REFRESH_SECRET);

//         const user = await User.findById(decoded.id);
//         if (!user) return res.status(404).json({ message: "User not found" });

//         // check refresh token in DB
//         const isValid = user.sessions.some(s => s.token === refreshToken);
//         if (!isValid) return res.status(401).json({ message: "Invalid refresh token" });

//         const newAccessToken = generateAccessToken(user);
//         res.json({ accessToken: newAccessToken });
//     } catch (err) {
//         res.status(401).json({ message: "Invalid or expired refresh token", error: err.message });
//     }
// };
module.exports = {
    Register,
    Login,
    Logout,
//  refreshAccessToken
};

const User = require("../model/user.model.js");
const jwt = require("jsonwebtoken");
require('dotenv').config();

const authMiddleware = async (req, res, next) => {
    try {
        const authHeader = req.headers.authorization;

        if (!authHeader || !authHeader.startsWith("Bearer ")) {
            return res.status(403).json({ message: "Authorization token missing or invalid" });
        }

        const token = authHeader.split(" ")[1];

        let decoded;
        try {
              // comment when generate access token use
            decoded = jwt.verify(token, process.env.SECRET);
            // decoded = jwt.verify(token, process.env.ACCESS_SECRET);
        } catch (err) {
            return res.status(401).json({ message: "Invalid or expired token" });
        }

         const user = await User.findById(decoded.id).select("-password");

        if (!user) {
            return res.status(404).json({ message: "User not found" });
        }
         // comment when generate access token use
       const isSessionValid = user.sessions.some(session => session.token === token);
        if (!isSessionValid) {
            return res.status(401).json({ message: "Session expired or token not registered" });
        }

        req.user = user;
        req.token = token;
        next();
    } catch (error) {
        console.error("authMiddleware error:", error);
        res.status(500).json({ message: "Server error during authentication" });
    }
};

const isAdmin = (req, res, next) => {
    if (!req.user || req.user.role !== 'Admin') {
        return res.status(403).json({ message: "Admin access required" });
    }
    next();
};

module.exports = { authMiddleware, isAdmin };

const mongoose = require("mongoose");
const User = require("../model/user.model.js");
const Task = require("../model/task.model.js");
const bcrypt = require("bcrypt");

const isValidId = (id) => mongoose.isValidObjectId(id);

const getAllUser = async (req, res) => {
    try {
        const userName = req.query.userName;
        const email = req.query.email;
        const limit = parseInt(req.query.limit) || 10;
        const page = parseInt(req.query.page) || 1;
        const sortBy = req.query.sortBy || 'createdAt';
        const orderBy = req.query.orderBy === 'asc' ? 1 : -1;

        const skip = (page - 1) * limit;

        const query = { role: 'User' };

        if (userName) {
            query.userName = new RegExp(userName, 'i');
        }
        if (email) {
            query.email = new RegExp(email, 'i');
        }

        const total = await User.countDocuments(query);
        const users = await User.find(query)
            .sort({ [sortBy]: orderBy })
            .skip(skip)
            .limit(limit);

        res.status(200).json({
            message: "All users fetched successfully",
            pageCount: Math.ceil(total / limit),
            total,
            limit,
            data: users
        });
    } catch (error) {
        console.error("Error while getting users:", error);
        res.status(500).json({ message: "Server error while getting all users" });
    }
};

const getUserById = async (req, res) => {
    try {
        if (!isValidId(req.params.id)) {
            return res.status(400).json({ message: "Invalid user ID" });
        }

        const user = await User.findById(req.params.id);
        if (!user) {
            return res.status(404).json({ message: "User not found" });
        }

        res.status(200).json({ message: "User fetched successfully", user });
    } catch (error) {
        console.error("Error while getting user by id", error);
        res.status(500).json({ message: "Server error while getting user" });
    }
};

const deleteUserById = async (req, res) => {
    try {
        if (!isValidId(req.params.id)) {
            return res.status(400).json({ message: "Invalid user ID" });
        }

        const deletedUser = await User.findByIdAndDelete(req.params.id);
        if (!deletedUser) {
            return res.status(404).json({ message: "User not found" });
        }

        res.status(200).json({ message: "User deleted successfully", deletedUser });
    } catch (error) {
        console.error("Error while deleting user", error);
        res.status(500).json({ message: "Server error while deleting user" });
    }
};

const updateUserById = async (req, res) => {
    try {
        const { id } = req.params;
        const { userName, email, password, role, birthDate } = req.body;

        const user = await User.findById(id);
        if (!user) {
            return res.status(404).json({ message: "User not found" });
        }

        if (role === "Admin") {
            const existingAdmin = await User.findOne({ role: "Admin", _id: { $ne: id } });
            if (existingAdmin) {
                return res.status(400).json({ message: "Another admin already exists" });
            }
        }

        if (email && email !== user.email) {
            const existingUser = await User.findOne({ email });
            if (existingUser) {
                return res.status(400).json({ message: "Email already in use" });
            }
            user.email = email;
        }
        if (userName) user.userName = userName;

        if (password) {
            user.password = await bcrypt.hash(password, 10);
        }

        if (role) user.role = role;
        if (birthDate && !isNaN(new Date(birthDate).getTime())) {
            user.birthDate = birthDate;
            const dob = new Date(birthDate);
            const diff = Date.now() - dob.getTime();
            user.age = new Date(diff).getUTCFullYear() - 1970;
        }

        if (req.file) {
            user.profileImage = `/uploads/${req.file.filename}`;
        }

        await user.save();

        res.status(200).json({
            message: "User updated successfully",
            user: {
                id: user._id,
                userName: user.userName,
                email: user.email,
                role: user.role,
                age: user.age,
                profileImage: user.profileImage
            }
        });
    } catch (error) {
        console.error("Update error:", error);
        res.status(500).json({ message: "Error while updating user", error: error.message });
    }
};

const getAllUserTask = async (req, res) => {
    try {
        const search = req.query.search || "";
        const limit = parseInt(req.query.limit) || 10;
        const page = parseInt(req.query.page) || 1;
        const sortBy = req.query.sortBy || 'createdAt';
        const orderBy = req.query.orderBy === 'asc' ? 1 : -1;

        const query = {
            'assignTask.userId': req.user.id,
            taskName: new RegExp(search, 'i'),
            isDeleted: false
        };

        const total = await Task.countDocuments(query);
        const tasks = await Task.find(query)
            .populate('createdBy', 'name email')
            .sort({ [sortBy]: orderBy })
            .skip((page - 1) * limit)
            .limit(limit);

        res.status(200).json({
            message: "Tasks fetched successfully",
            tasks,
            pageCount: Math.ceil(total / limit),
            limit
        });
    } catch (error) {
        console.error("Error while getting user tasks", error);
        res.status(500).json({ message: "Server error while fetching tasks" });
    }
};

const updateStatus = async (req, res) => {
    try {
        const { status, id } = req.body;
        const { taskId } = req.params;

        if (!isValidId(taskId) || !isValidId(id)) {
            return res.status(400).json({ message: "Invalid task or assignment ID" });
        }

        const allowedStatuses = ["Pending", "In Progress", "Completed"];
        if (!status || !allowedStatuses.includes(status)) {
            return res.status(400).json({ message: "Invalid status value" });
        }

        const task = await Task.findOneAndUpdate(
            { _id: taskId, 'assignTask._id': id, 'assignTask.userId': req.user.id },
            { $set: { 'assignTask.$.status': status } },
            { new: true }
        );

        if (!task) {
            return res.status(404).json({ message: "Task not found or not assigned to you" });
        }

        res.status(200).json({ message: "Task status updated successfully", task });
    } catch (error) {
        console.error("Error while updating task status", error);
        res.status(500).json({ message: "Server error while updating task status" });
    }
};

const commentTask = async (req, res) => {
    try {
        const { taskId } = req.params;
        const { comment } = req.body;
        const userId = req.user.id;

        if (!isValidId(taskId)) {
            return res.status(400).json({ message: "Invalid task ID" });
        }

        if (!comment && !req.file) {
            return res.status(400).json({ message: "Comment or file is required" });
        }

        const filePath = req.file ? `/uploads/${req.file.filename}` : null;

        const task = await Task.findOneAndUpdate(
            { _id: taskId, 'assignTask.userId': userId },
            {
                $push: {
                    comments: {
                        userId,
                        media: filePath,
                        comment
                    }
                }
            },
            { new: true }
        );

        if (!task) {
            return res.status(404).json({ message: "Task not found or not assigned to you" });
        }

        res.status(200).json({ message: "Comment added successfully", task });
    } catch (error) {
        console.error("Error while adding comment", error);
        res.status(500).json({ message: "Server error while adding comment" });
    }
};

module.exports = {
    deleteUserById,
    updateUserById,
    getAllUser,
    getUserById,
    updateStatus,
    getAllUserTask,
    commentTask
};

const mongoose = require("mongoose");
const Task = require("../model/task.model.js");
const User = require("../model/user.model.js");
const sendHtmlMail = require("../utils/mail.js");
const path = require("path");

const createTask = async (req, res) => {
    try {
        const { taskName, description } = req.body;

        if (!taskName || !description) {
            return res.status(400).json({ message: "Task name and description are required" });
        }

        const isTask = await Task.findOne({ taskName: { $regex: `^${taskName}$`, $options: "i" } });
        if (isTask) {
            return res.status(400).json({ message: "Task already exists" });
        }

        const task = new Task({
            taskName,
            description,
            createdBy: req.user.id
        });

        await task.save();

        res.status(201).json({ message: "Task created successfully", task });
    } catch (error) {
        console.error("Error while creating task:", error);
        res.status(500).json({ message: "Error while creating task", error: error.message });
    }
};


const getAllTask = async (req, res) => {
    try {
        const taskName = req.query.taskName || "";
        const limit = parseInt(req.query.limit) || 10;
        const page = parseInt(req.query.page) || 1;
        const sortBy = req.query.sortBy || "createdAt";
        const orderBy = req.query.orderBy === "asc" ? 1 : -1;

        const query = { isDeleted: false };

        if (taskName) {
            query.taskName = new RegExp(taskName, "i");
        }

        const total = await Task.countDocuments(query);
        const tasks = await Task.find(query)
            .populate("createdBy", "name email")
            .sort({ [sortBy]: orderBy })
            .skip((page - 1) * limit)
            .limit(limit);

        res.status(200).json({
            message: "Tasks fetched successfully",
            tasks,
            pageCount: Math.ceil(total / limit),
            total
        });
    } catch (error) {
        console.error("Error while fetching tasks:", error);
        res.status(500).json({ message: "Error while fetching tasks", error: error.message });
    }
};


const getTaskById = async (req, res) => {
    try {
        if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
            return res.status(400).json({ message: "Invalid task ID" });
        }

        const task = await Task.findOne({ _id: req.params.id, isDeleted: false });

        if (!task) {
            return res.status(404).json({ message: "Task not found" });
        }

        res.status(200).json({ message: "Task fetched successfully", task });
    } catch (error) {
        console.error("Error while getting task by ID:", error);
        res.status(500).json({ message: "Error while getting task by ID", error: error.message });
    }
};



const deleteTaskById = async (req, res) => {
    try {
        if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
            return res.status(400).json({ message: "Invalid task ID" });
        }

        const deletedTask = await Task.findByIdAndDelete(req.params.id);
        if (!deletedTask) {
            return res.status(404).json({ message: "Task not found" });
        }

        res.status(200).json({ message: "Task deleted successfully", deletedTask });
    } catch (error) {
        console.error("Error while deleting task:", error);
        res.status(500).json({ message: "Error while deleting task", error: error.message });
    }
};

const updateTaskById = async (req, res) => {
    try {
        if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
            return res.status(400).json({ message: "Invalid task ID" });
        }

        const updatedTask = await Task.findOneAndUpdate(
            { _id: req.params.id, isDeleted: false },
            req.body,
            { new: true }
        );

        if (!updatedTask) {
            return res.status(404).json({ message: "Task not found or already deleted" });
        }

        res.status(200).json({ message: "Task updated successfully", updatedTask });
    } catch (error) {
        console.error("Error while updating task:", error);
        res.status(500).json({ message: "Error while updating task", error: error.message });
    }
};


const softDeleteTask = async (req, res) => {
    try {
        if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
            return res.status(400).json({ message: "Invalid task ID" });
        }

        const task = await Task.findByIdAndUpdate(req.params.id, { isDeleted: true }, { new: true });
        if (!task) {
            return res.status(404).json({ message: "Task not found" });
        }

        res.status(200).json({ message: "Task soft-deleted successfully", task });
    } catch (error) {
        console.error("Error while soft-deleting task:", error);
        res.status(500).json({ message: "Error while soft-deleting task", error: error.message });
    }
};


const assignTask = async (req, res) => {
    try {
        const { userIds } = req.body;

        if (!Array.isArray(userIds) || userIds.length === 0) {
            return res.status(400).json({ message: "User IDs are required and must be an array" });
        }

        const users = await User.find({ _id: { $in: userIds }, role: "User" });

        if (userIds.length !== users.length) {
            return res.status(400).json({ message: "One or more user IDs are invalid or not 'User' role" });
        }

        const task = await Task.findOne({ _id: req.params.id, isDeleted: false });
        if (!task) {
            return res.status(404).json({ message: "Task not found or already deleted" });
        }

        const alreadyAssigned = task.assignTask.map(a => a.userId.toString());
        const newUsers = users.filter(user => !alreadyAssigned.includes(user._id.toString()));

        if (newUsers.length === 0) {
            return res.status(400).json({ message: "All provided users are already assigned to this task" });
        }

        const validUsers = newUsers.map(user => ({
            userId: user._id,
            status: "Pending",
        }));

        task.assignTask.push(...validUsers);
        await task.save();

        res.status(200).json({ message: "Task assigned successfully", task });
    } catch (error) {
        console.error("Error while assigning task:", error);
        res.status(500).json({ message: "Error while assigning task", error: error.message });
    }
};


module.exports = {
    createTask,
    getTaskById,
    updateTaskById,
    deleteTaskById,
    getAllTask,
    softDeleteTask,
    assignTask,
}

const { validationResult } = require('express-validator');


const validate = (req, res, next) => {
    const errors = validationResult(req);

    if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
    }
    next();
}

module.exports = validate;

const { body, param, query } = require("express-validator");

exports.registerValidator = [
    body('userName')
        .notEmpty().withMessage('User name is required')
        .isLength({ min: 3 }).withMessage('User name must be at least 3 characters long'),

    body('email')
        .isEmail().withMessage('Valid email is required'),

    body('password')
        .isLength({ min: 6 }).withMessage('Password must be at least 6 characters long'),

    body('role')
        .isIn(['Admin', 'User']).withMessage('Role must be either Admin or User'),
];


exports.loginValidator = [
    body('email')
        .isEmail().withMessage('Valid email is required'),

    body('password')
        .notEmpty().withMessage('Password is required')
];

exports.updateUserValidator = [
    param('id').isMongoId().withMessage("Invalid user ID"),
    body('userName').optional().isLength({ min: 3 }).withMessage("User name must be at least 3 characters"),
    body('email').optional().isEmail().withMessage("Valid email is required"),
    body('password').optional().isLength({ min: 6 }).withMessage("Password must be at least 6 characters"),
];

exports.getUserByIdValidator = [
    param('id').isMongoId().withMessage("Invalid user ID")
];

exports.deleteUserValidator = [
    param('id').isMongoId().withMessage("Invalid user ID")
];

exports.updateStatusValidator = [
    param('taskId').isMongoId().withMessage("Invalid Task ID"),
    body('id').isMongoId().withMessage("Invalid Assign Task ID"),
    body('status').isIn(['Pending', 'Ongoing', 'Completed']).withMessage("Invalid status value"),
];

exports.commentTaskValidator = [
    param('taskId').isMongoId().withMessage("Invalid Task ID"),
    body('comment').notEmpty().withMessage("Comment is required")
];

exports.createTaskValidator = [
    body("taskName")
        .notEmpty().withMessage("Task name is required")
        .isLength({ min: 3 }).withMessage("Task name must be at least 3 characters"),

    body("description")
        .notEmpty().withMessage("Description is required")
];

exports.updateTaskValidator = [
    param("id").isMongoId().withMessage("Invalid Task ID"),
    body("taskName").optional().isLength({ min: 3 }).withMessage("Task name must be at least 3 characters"),
    body("description").optional().isLength({ min: 3 }).withMessage("Description must be at least 3 characters")
];

exports.taskIdValidator = [
    param("id").isMongoId().withMessage("Invalid Task ID")
];

exports.assignTaskValidator = [
    param("id").isMongoId().withMessage("Invalid Task ID"),
    body("userIds")
        .isArray({ min: 1 }).withMessage("userIds must be a non-empty array")
        .custom((arr) => arr.every(id => typeof id === "string")).withMessage("Each userId must be a string")
];

exports.getTaskQueryValidator = [
    query("limit").optional().isInt({ min: 1 }).withMessage("limit must be a positive number"),
    query("page").optional().isInt({ min: 1 }).withMessage("page must be a positive number"),
    query("sortBy").optional().isString().withMessage("sortBy must be a string"),
    query("orderBy").optional().isIn(["asc", "desc"]).withMessage("orderBy must be 'asc' or 'desc'")
];

const nodemailer = require("nodemailer");
const ejs = require("ejs");

const transporter = nodemailer.createTransport({
    host: "smtp.mailtrap.io",
    port: 587,
    auth: {
        user: "47e27c7dace3d1",
        pass: "4afd701b115d55",
    },
});

const sendHtmlMail = async (to, subject, templatePath, data) => {
    try {
        const html = await ejs.renderFile(templatePath, data);

        const mailOptions = {
            from: '"Task System" <no-reply@task.com>',
            to,
            subject,
            html,
        };

        const result = await transporter.sendMail(mailOptions);
        console.log("Email sent:", result.messageId);
        return result;
    } catch (err) {
        console.error(" Failed to send email:", err.message);
        throw err;
    }
};

module.exports = sendHtmlMail;

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>New Task Assigned</title>
  <style>
    body { font-family: Arial, sans-serif; color: #333; }
    .container { padding: 20px; background: #f9f9f9; border-radius: 10px; }
    h2 { color: #007bff; }
  </style>
</head>
<body>
  <div class="container">
    <h2>Hello <%= userName %>,</h2>
    <p>You have been assigned a new task:</p>
    <ul>
      <li><strong>Task Name:</strong> <%= taskName %></li>
      <li><strong>Description:</strong> <%= description %></li>
    </ul>
    <p>Please check your task list in the system.</p>
    <br>
    <p>Thanks,<br/>Task Management Team</p>
  </div>
</body>
</html>