Repository: MazenShoaip/Task-Manager-Backend Branch: main Commit: 47a38a1a2cf9 Files: 47 Total size: 23.0 KB Directory structure: gitextract_zwmc_rwj/ ├── .gitignore ├── main.js ├── package.json ├── server.js └── src/ ├── app.js ├── controllers/ │ ├── add-task-controller.js │ ├── edit-task-controller.js │ ├── get-tasks-controller.js │ ├── login-controller.js │ ├── refresh-token-controller.js │ ├── remove-task-controller.js │ ├── renew-otp-controller.js │ ├── signup-controller.js │ ├── submit-task-controller.js │ └── verify-user-controller.js ├── middleware/ │ └── verify-token.js ├── models/ │ └── database.js ├── routes/ │ ├── add-task-route.js │ ├── edit-task-route.js │ ├── get-tasks-route.js │ ├── login-route.js │ ├── refresh-token-route.js │ ├── remove-task-route.js │ ├── renew-otp-route.js │ ├── signup-route.js │ ├── submit-task-route.js │ └── verify-user.js ├── schemas/ │ ├── otpSchema.js │ ├── submitSchema.js │ ├── taskSchema.js │ ├── userLoginSchema.js │ ├── userSignupSchema.js │ └── verifyUserSchema.js └── services/ ├── add-task-service.js ├── create-tokens.js ├── edit-task-service.js ├── email-service.js ├── errors.js ├── get-task-service.js ├── login-service.js ├── refresh-token-service.js ├── remove-task-service.js ├── renew-otp-service.js ├── signup-service.js ├── submit-task-service.js ├── throw-error.js └── verify-user-service.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .env node_modules .cph ================================================ FILE: main.js ================================================ ================================================ FILE: package.json ================================================ { "name": "task-manager", "version": "1.0.0", "description": "", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "bcrypt": "^6.0.0", "cookie-parser": "^1.4.7", "dotenv": "^17.4.1", "express": "^5.2.1", "express-rate-limit": "^8.4.1", "jsonwebtoken": "^9.0.3", "mongodb": "^7.1.1", "nodemailer": "^8.0.5", "nodemon": "^3.1.14", "zod": "^4.3.6" } } ================================================ FILE: server.js ================================================ const db = require("./src/models/database"); const app = require("./src/app"); (async () => { await db.connectToMongo(); app.listen(5000); console.log("server ready"); })(); ================================================ FILE: src/app.js ================================================ const express = require("express"); const rateLimit = require("express-rate-limit"); let app = express(); const globalLimiter = rateLimit({ windowMs: 60 * 1000, // 1 min max: 100, // reasonable default message: "Too many requests", }); app.use(globalLimiter); const login = require("./routes/login-route.js"); const signup = require("./routes/signup-route.js"); const refresh = require("./routes/refresh-token-route.js"); const removeTask = require("./routes/remove-task-route.js"); const editTask = require("./routes/edit-task-route.js"); const addTask = require("./routes/add-task-route.js"); const getTasks = require("./routes/get-tasks-route.js"); const submitTask = require("./routes/submit-task-route.js"); const verifyUser = require("./routes/verify-user.js"); const renewOTP = require("./routes/renew-otp-route.js"); const cookieParser = require("cookie-parser"); const errors = require("./services/errors.js"); app.use(cookieParser()); app.use(express.json()); app.use("/signup", signup); app.use("/verify/", verifyUser); app.use("/login", login); app.use("/token/refresh", refresh); app.use("/task", addTask); app.use("/task", removeTask); app.use("/task", editTask); app.use("/tasks", getTasks); app.use("/submit/", submitTask); app.use("/otp/", renewOTP); app.use(errors); module.exports = app; ================================================ FILE: src/controllers/add-task-controller.js ================================================ let addTaskService = require("../services/add-task-service"); async function addTask(req, res) { let result = await addTaskService(req.body, req.user); res.status(result.status).send(result.message); } module.exports = addTask; ================================================ FILE: src/controllers/edit-task-controller.js ================================================ let editTaskService = require("../services/edit-task-service"); async function editTask(req, res) { let result = await editTaskService(req.body,req.params.id, req.user); res.status(result.status).send(result.message); } module.exports = editTask; ================================================ FILE: src/controllers/get-tasks-controller.js ================================================ const getTasksService = require("../services/get-task-service"); async function getTasks(req, res) { let tasks = await getTasksService(req.user); res.status(tasks.status).json(tasks.tasks); } module.exports = getTasks; ================================================ FILE: src/controllers/login-controller.js ================================================ const loginService = require("../services/login-service"); async function login(req, res) { let result = await loginService(req.body); res.status(result.status) .cookie("refreshToken", result.tokens.refresh, { httpOnly: true, secure: false, sameSite: "strict", path: "/token/refresh", }) .json({ accessToken: result.tokens.access }); } module.exports = login; ================================================ FILE: src/controllers/refresh-token-controller.js ================================================ const refreshTokenService = require("../services/refresh-token-service"); async function refreshToken(req, res) { let result = await refreshTokenService(req.cookies.refreshToken); res.cookie("refreshToken", result.tokens.refresh, { httpOnly: true, secure: false, sameSite: "strict", path: "/token/refresh", }); res.status(result.status).json({ "access token": result.tokens.access }); } module.exports = refreshToken; ================================================ FILE: src/controllers/remove-task-controller.js ================================================ const db = require("../models/database"); const removeTaskService = require("../services/remove-task-service"); async function removeTask(req, res) { let result = await removeTaskService(req.params.id, req.user); res.status(result.status).send(result.message); } module.exports = removeTask; ================================================ FILE: src/controllers/renew-otp-controller.js ================================================ const renewOTPService = require("../services/renew-otp-service"); async function renewOTP(req, res) { let result = await renewOTPService(req.body); res.status(result.status).send(result.message); } module.exports = renewOTP; ================================================ FILE: src/controllers/signup-controller.js ================================================ const signupService = require("../services/signup-service"); async function signup(req, res) { let result = await signupService(req.body); res.status(result.status).send(result.message); } module.exports = signup; ================================================ FILE: src/controllers/submit-task-controller.js ================================================ const submitTaskService = require("../services/submit-task-service"); async function submitTask(req, res) { let result = await submitTaskService(req.body, req.params.id, req.user); res.status(result.status).send(result.message); } module.exports = submitTask; ================================================ FILE: src/controllers/verify-user-controller.js ================================================ const verifyUserService = require("../services/verify-user-service"); async function verifyUser(req, res) { let result = await verifyUserService(req.body); res.status(result.status).send(result.message); } module.exports = verifyUser; ================================================ FILE: src/middleware/verify-token.js ================================================ const jwt = require("jsonwebtoken"); const throwError = require("../services/throw-error"); require("dotenv").config(); function verifyToken(req, res, next) { let token = String(req.headers.authorization).split(" ")[1]; let user; try { user = jwt.verify(token, process.env.JWT_KEY); } catch (e) { throwError(401, "Access Denied"); } if (user.type !== "access") throwError(401, "Access Denied"); req.user = user; // res.send(user); next(); } module.exports = verifyToken; ================================================ FILE: src/models/database.js ================================================ require("dotenv").config(); const uri = process.env.MONGO_URI; const { MongoClient, ServerApiVersion, ObjectId } = require("mongodb"); // Create a MongoClient with a MongoClientOptions object to set the Stable API version const mongo = new MongoClient(uri, { connectTimeoutMS: 5000, serverSelectionTimeoutMS: 5000, socketTimeoutMS: 5000, serverApi: { version: ServerApiVersion.v1, strict: true, deprecationErrors: true, }, }); let db; async function connectToMongo() { try { await mongo.connect(); // mongo.db("database").dropDatabase(); db = mongo.db("database"); await db .collection("pendingUsers") .createIndex({ createdAt: 1 }, { expireAfterSeconds: 300 }); db.collection("users").createIndex({ userName: 1 }, { unique: true }); db.collection("users").createIndex({ email: 1 }, { unique: true }); console.log(await db.collection("pendingUsers").find({}).toArray()); } catch (e) { console.error(e); process.exit(1); } } async function insertUser(data) { let users = db.collection("pendingUsers"); data.createdAt = new Date(); return await users.insertOne(data); } async function insertVerifiedUser(data) { let users = db.collection("users"); return await users.insertOne(data); } async function getUser(addedUser) { let users = db.collection("users"); let user = await users.findOne(addedUser); return user; } async function getPendingUser(u) { let users = db.collection("pendingUsers"); let user = await users.findOne(u); return user; } async function refreshPendingUser(id, otp) { let users = db.collection("pendingUsers"); let result = await users.findOneAndUpdate( { _id: new ObjectId(id) }, { $set: { otp: otp, createdAt: new Date() } }, ); return result; } async function deletePendingUser(u) { let users = db.collection("pendingUsers"); let user = await users.findOneAndDelete(u); return user; } async function getUsers() { let users = db.collection("users"); let allUsers = await users.find({}).toArray(); return allUsers; } async function insertToken(jti) { let tokens = db.collection("tokens"); await tokens.insertOne({ jti: jti }); } async function getToken(jti) { let tokens = db.collection("tokens"); let token = await tokens.findOne({ jti: jti }); return token ? true : false; } async function removeToken(jti) { let tokens = db.collection("tokens"); let token = await tokens.findOneAndDelete({ jti: jti }); return token; } async function insertTask(task) { let tasks = db.collection("tasks"); await tasks.insertOne(task); } async function editTask(task, id) { let tasks = db.collection("tasks"); let result = await tasks.findOneAndUpdate( { _id: new ObjectId(id) }, { $set: task }, ); return result; } async function removeTask(id) { let tasks = db.collection("tasks"); let result = await tasks.findOneAndDelete({ _id: new ObjectId(id) }); return result; } async function getTask(id) { let tasks = db.collection("tasks"); let result = await tasks.findOne({ _id: new ObjectId(id) }); return result; } async function submitTask(id, submit) { let tasks = db.collection("tasks"); let result = await tasks.findOneAndUpdate( { _id: new ObjectId(id) }, { $set: { submit: submit.task } }, ); return result; } async function getTasks(to) { let tasks = db.collection("tasks"); let tasksList = await tasks.find({ to: to }).toArray(); return tasksList; } module.exports = { connectToMongo, insertUser, insertVerifiedUser, getUsers, getUser, getPendingUser, deletePendingUser, refreshPendingUser, insertToken, getToken, removeToken, insertTask, getTask, getTasks, editTask, removeTask, submitTask, }; ================================================ FILE: src/routes/add-task-route.js ================================================ let router = require("express").Router(); const addTask = require("../controllers/add-task-controller"); let verifyToken = require("../middleware/verify-token"); router.post("/", verifyToken, addTask); module.exports = router; ================================================ FILE: src/routes/edit-task-route.js ================================================ let router = require("express").Router(); const editTask = require("../controllers/edit-task-controller"); let verifyToken = require("../middleware/verify-token"); router.put("/:id", verifyToken, editTask); module.exports = router; ================================================ FILE: src/routes/get-tasks-route.js ================================================ let router = require("express").Router(); const getTasks = require("../controllers/get-tasks-controller"); let verifyToken = require("../middleware/verify-token"); router.get("/", verifyToken, getTasks); module.exports = router; ================================================ FILE: src/routes/login-route.js ================================================ const login = require("../controllers/login-controller"); const router = require("express").Router(); router.post("/", login); module.exports = router; ================================================ FILE: src/routes/refresh-token-route.js ================================================ const refreshToken = require("../controllers/refresh-token-controller"); let router = require("express").Router(); router.post("/", refreshToken); module.exports = router; ================================================ FILE: src/routes/remove-task-route.js ================================================ let router = require("express").Router(); const removeTask = require("../controllers/remove-task-controller"); let verifyToken = require("../middleware/verify-token"); router.delete("/:id", verifyToken, removeTask); module.exports = router; ================================================ FILE: src/routes/renew-otp-route.js ================================================ const renewOTP = require("../controllers/renew-otp-controller"); let router = require("express").Router(); router.post("/", renewOTP); module.exports = router; ================================================ FILE: src/routes/signup-route.js ================================================ const signup = require("../controllers/signup-controller"); let router = require("express").Router(); router.post("/", signup); module.exports = router; ================================================ FILE: src/routes/submit-task-route.js ================================================ let router = require("express").Router(); let verifyToken = require("../middleware/verify-token"); let submitTaskController = require("../controllers/submit-task-controller"); router.post("/:id", verifyToken, submitTaskController); module.exports = router; ================================================ FILE: src/routes/verify-user.js ================================================ const verifyUser = require("../controllers/verify-user-controller"); let router = require("express").Router(); router.post("/", verifyUser); module.exports = router; ================================================ FILE: src/schemas/otpSchema.js ================================================ const { z } = require("zod"); let otpSchema = z.object({ id: z.string(), }); module.exports = otpSchema; ================================================ FILE: src/schemas/submitSchema.js ================================================ let { z } = require("zod"); let submitSchema = z.object({ task: z.string().min(3) }); module.exports = submitSchema; ================================================ FILE: src/schemas/taskSchema.js ================================================ let { z } = require("zod"); let taskSchema = z.object({ title: z.string().min(3), task: z.string().min(10), from: z.string().min(3), to: z.string().min(3), }); let a; module.exports = taskSchema; ================================================ FILE: src/schemas/userLoginSchema.js ================================================ const { z } = require("zod"); let userSchema = z .object({ userName: z.string().min(3).optional(), password: z.string().min(8), email: z.string().email().optional(), role: z.enum(["admin", "moderator", "user"]), }) .refine( (data) => data.userName || data.email, { message: "Provide either userName or email" }, ); module.exports = userSchema; ================================================ FILE: src/schemas/userSignupSchema.js ================================================ const { z } = require("zod"); let userSchema = z.object({ userName: z.string().min(3), password: z.string().min(8), email: z.string().email("Invalid Email").min(5), role: z.enum(["admin", "moderator", "user"]), }); module.exports = userSchema; ================================================ FILE: src/schemas/verifyUserSchema.js ================================================ const { z } = require("zod"); let verifyUserSchema = z.object({ id: z.string(), otp: z.string().min(6).max(6), }); module.exports = verifyUserSchema; ================================================ FILE: src/services/add-task-service.js ================================================ const { insertTask } = require("../models/database"); const throwError = require("./throw-error"); const taskSchema = require("../schemas/taskSchema"); async function addTaskService(body, user) { if (user.role !== "admin") { throwError(401, "Denied"); } body.from = user.userName; let verifiedData = taskSchema.safeParse(body); if (!verifiedData.success) { throwError(400, verifiedData.error.message); } await insertTask(verifiedData.data); return { status: 201, message: "Task is sent" }; } module.exports = addTaskService; ================================================ FILE: src/services/create-tokens.js ================================================ require("dotenv").config(); const jwt = require("jsonwebtoken"); const crypto = require("crypto"); let { insertToken } = require("../models/database"); function createTokens(user) { let jti = crypto.randomUUID(); let jtiA = crypto.randomUUID(); let idData = {}; if (user.userName) idData.userName = user.userName; else idData.email = user.email; let refresh = jwt.sign( { jti: jti, type: "refresh", idData: idData, email: user.email, role: user.role, }, process.env.JWT_KEY, { expiresIn: "7d", }, ); insertToken(jti); let access = jwt.sign( { jti: jtiA, type: "access", idData: idData, role: user.role, }, process.env.JWT_KEY, { expiresIn: "1h", }, ); return { access: access, refresh: refresh }; } module.exports = createTokens; ================================================ FILE: src/services/edit-task-service.js ================================================ const db = require("../models/database"); const taskSchema = require("../schemas/taskSchema"); const throwError = require("./throw-error"); async function editTaskService(body, taskID, user) { if (user.role !== "admin") throwError(401, "Denied"); body.from = user.userName; let verifiedData = taskSchema.safeParse(body); if (!verifiedData.success) throwError(400, verifiedData.error.message); let updated = await db.editTask(verifiedData.data, taskID); if (!updated) throwError(400, "Task Does not exist"); return { status: 200, message: "Task is updated" }; } module.exports = editTaskService; ================================================ FILE: src/services/email-service.js ================================================ let nodemailer = require("nodemailer"); require("dotenv").config(); const transporter = nodemailer.createTransport({ host: process.env.EMAIL_HOST, port: 587, auth: { user: process.env.EMAIL_USER, pass: process.env.EMAIL_PASSWORD, }, }); async function sendEmail(mail) { const info = await transporter.sendMail({ from: process.env.EMAIL_USER, to: mail.to, subject: mail.subject, text: mail.text, }); // console.log("Message sent:", info.messageId); console.log("Preview URL:", nodemailer.getTestMessageUrl(info)); return "message sent"; } // sendEmail({ to: "test1@user.com", subject: "test subject", text: "text ENV" }); module.exports = sendEmail; ================================================ FILE: src/services/errors.js ================================================ function errors(err, req, res, next) { res.status(err.status || 500).json({ success: false, message: err.message || "Internal server error", }); } module.exports = errors; ================================================ FILE: src/services/get-task-service.js ================================================ const { getTasks } = require("../models/database"); const throwError = require("./throw-error"); async function getTasksService(user) { if (!user) throwError(401, "Denied"); let tasks = await getTasks(user.userName); return { status: 200, tasks: tasks }; } module.exports = getTasksService; ================================================ FILE: src/services/login-service.js ================================================ const { getUser, getUsers } = require("../models/database"); const bcrypt = require("bcrypt"); const createTokens = require("../services/create-tokens"); const userSchema = require("../schemas/userLoginSchema"); let throwError = require("./throw-error"); async function loginService(body) { body.role = "user"; let verified = userSchema.safeParse(body); if (!verified.success) throwError(400, "Invalid Login"); let loginData = verified.data; // let users = await getUsers(); // console.log(users); let idData = {}; if (loginData.userName) idData.userName = loginData.userName; else idData.email = loginData.email; console.log(loginData); let user = await getUser(idData); if (!user) throwError(400, "Not found"); let match = await bcrypt.compare(loginData.password, user.password); if (!match) throwError(400, "Password didn't match"); let tokens = createTokens(user); return { status: 200, tokens: tokens }; } module.exports = loginService; ================================================ FILE: src/services/refresh-token-service.js ================================================ const jwt = require("jsonwebtoken"); const { getToken } = require("../models/database"); const createTokens = require("../services/create-tokens"); const throwError = require("./throw-error"); require("dotenv").config(); async function refreshTokenService(token) { let tokenInfo = jwt.verify(token, process.env.JWT_KEY); let dbToken = await getToken(tokenInfo.jti); if (!dbToken || tokenInfo.type !== "refresh") { throwError(401, "Access Denied"); } let tokens = createTokens(tokenInfo); return { status: 200, tokens: tokens }; } module.exports = refreshTokenService; ================================================ FILE: src/services/remove-task-service.js ================================================ const db = require("../models/database"); const throwError = require("./throw-error"); async function removeTaskService(taskID, user) { if (user.role !== "admin") throwError(401, "Denied"); let removedTask = await db.removeTask(taskID); if (!removedTask) throwError(400, "Task does not exist"); return { status: 200, message: "Task removed" }; } module.exports = removeTaskService; ================================================ FILE: src/services/renew-otp-service.js ================================================ const { insertUser, getPendingUser, refreshPendingUser, } = require("../models/database"); const bcrypt = require("bcrypt"); const crypto = require("crypto"); const otpSchema = require("../schemas/otpSchema"); const throwError = require("./throw-error"); const sendEmail = require("./email-service"); const { ObjectId } = require("mongodb"); async function renewOTPService(body) { let verified = otpSchema.safeParse(body); if (!verified.success) throwError(400, "Invalid Data"); let otpRenewData = verified.data; let findUser = await getPendingUser({ _id: new ObjectId(otpRenewData.id), }); if (!findUser) return throwError(400, "Does not exist"); let otp = crypto.randomInt(100000, 999999).toString(); let hashedOTP = await bcrypt.hash(otp, 10); await refreshPendingUser(verified.data.id, hashedOTP); await sendEmail({ to: findUser.email, subject: "Renewed OTP Key", text: otp, }); return { status: 201, message: { id: findUser.insertedId } }; } module.exports = renewOTPService; ================================================ FILE: src/services/signup-service.js ================================================ const { getUser, insertUser, getPendingUser } = require("../models/database"); const bcrypt = require("bcrypt"); const crypto = require("crypto"); const userSchema = require("../schemas/userSignupSchema"); const throwError = require("./throw-error"); const sendEmail = require("./email-service"); async function signupService(body) { body.role = "user"; let verified = userSchema.safeParse(body); if (!verified.success) throwError(400, "Invalid Data"); let signupData = verified.data; let findUser = await getUser({ userName: signupData.userName }); if (findUser) return throwError(409, "Already used username"); let findPendingUser = await getPendingUser({ userName: signupData.userName, }); if (findPendingUser) return throwError(409, "Already used username"); let findEmail = await getUser({ email: signupData.email }); if (findEmail) return throwError(409, "Already used Email"); let encrypted = await bcrypt.hash(signupData.password, 10); signupData.password = encrypted; // signupData.role = 'admin'; let otp = crypto.randomInt(100000, 999999).toString(); signupData.otp = await bcrypt.hash(otp, 10); let user = await insertUser(signupData); await sendEmail({ to: signupData.email, subject: "OTP Key", text: otp }); return { status: 201, message: { id: user.insertedId } }; } module.exports = signupService; ================================================ FILE: src/services/submit-task-service.js ================================================ let db = require("../models/database"); let throwError = require("./throw-error"); const submitSchema = require("../schemas/submitSchema"); async function submitTaskService(body, taskID, user) { if (!user) throwError(401, "Denied"); let task = await db.getTask(taskID); if (!task) throwError(400, "Task not found"); if (user.userName != task.to) return throwError(401, "Denied"); let verifiedSubmit = submitSchema.safeParse(body); if (!verifiedSubmit.success) throwError(400, verifiedSubmit.error.message); let data = verifiedSubmit.data; await db.submitTask(taskID, data); return { status: 201, message: "submitted" }; } module.exports = submitTaskService; ================================================ FILE: src/services/throw-error.js ================================================ function throwError(status, message) { let err = new Error(message); err.status = status; throw err; } module.exports = throwError; ================================================ FILE: src/services/verify-user-service.js ================================================ const { getUser, insertVerifiedUser, deletePendingUser, } = require("../models/database"); const bcrypt = require("bcrypt"); const verifyUserSchema = require("../schemas/verifyUserSchema"); const { getPendingUser } = require("../models/database"); const throwError = require("./throw-error"); const { ObjectId } = require("mongodb"); async function verifyUserService(body) { let verifyData = verifyUserSchema.safeParse(body); if (!verifyData.success) return throwError(400, "Invalid Data"); let verifyUserData = verifyData.data; let user = await getPendingUser({ _id: new ObjectId(verifyUserData.id) }); if (!user) return throwError(400, "Wrong id"); let matchedOTP = await bcrypt.compare(verifyUserData.otp, user.otp); console.log(matchedOTP); if (!matchedOTP) return throwError(400, "Wrong OTP"); let createdAt = new Date(user.createdAt).getTime(); if (Date.now() > 300 * 1000 + createdAt) return throwError(400, "Expired OTP"); delete user.otp; let foundUser = await getUser({ email: user.email }); if (foundUser) return throwError(400, "Already verified"); let signed = await insertVerifiedUser(user); await deletePendingUser({ _id: new ObjectId(verifyUserData.id) }); return { status: 201, message: "OK" }; } module.exports = verifyUserService;