diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 31c45b9..b9c9210 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,8 +5,8 @@ name: Deploy on push on: # Triggers the workflow on push or pull request events but only for the "main" branch push: - branches: [ "main" ] - + branches: ["main"] + # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/README.md b/README.md index 14bf73a..7f436af 100644 --- a/README.md +++ b/README.md @@ -3,15 +3,15 @@ Refer to .env.local file and create your own .env file as required # Backup Overview 1. **Backup Existence**: The project has a robust backup system in place to ensure data integrity and availability. -2. **MongoDB Backup**: - - **Scope**: All user accounts, passwords, blogs, and other data (excluding images) are backed up. - - **Frequency**: Backups are performed daily. - - **Storage**: The backups are stored on the server and uploaded to the Telegram channel. +2. **MongoDB Backup**: + - **Scope**: All user accounts, passwords, blogs, and other data (excluding images) are backed up. + - **Frequency**: Backups are performed daily. + - **Storage**: The backups are stored on the server and uploaded to the Telegram channel. 3. **Image Backup**: - - **Scope**: All image data is backed up. - - **Frequency**: Backups are performed twice a month (every 15 days). - - **Storage**: Image backups are stored on the server. - - **Notifications**: Notifications are delivered on the same Telegram channel whenever an image backup is completed. + - **Scope**: All image data is backed up. + - **Frequency**: Backups are performed twice a month (every 15 days). + - **Storage**: Image backups are stored on the server. + - **Notifications**: Notifications are delivered on the same Telegram channel whenever an image backup is completed. ## Accessing Server Backups @@ -19,6 +19,7 @@ To access server backups, log in using the following credentials: - **Username**: dvishal485 - Managed via cron jobs + ```bash crontab -e ``` diff --git a/package-lock.json b/package-lock.json index 223c903..4cfd6fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -809,7 +809,7 @@ }, "node_modules/@types/serve-static": { "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-@static/-/serve-static-1.15.7.tgz", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "dev": true, "license": "MIT", @@ -4230,9 +4230,10 @@ "license": "MIT" }, "node_modules/multer": { - "version": "1.4.5-lts.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", - "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "version": "1.4.5-lts.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", + "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", + "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.", "license": "MIT", "dependencies": { "append-field": "^1.0.0", diff --git a/src/api/controllers/authController.ts b/src/api/controllers/authController.ts index 6e63661..d67f734 100644 --- a/src/api/controllers/authController.ts +++ b/src/api/controllers/authController.ts @@ -142,7 +142,7 @@ export const refresh = asyncErrorHandler(async (req, res, next) => { */ export const signup = asyncErrorHandler(async (req, res, next) => { - const { name, username: email } = req.body; + const { name, username: email, teamRole: role_id } = req.body; if (!email || !name) { const error = new CustomError( @@ -153,7 +153,6 @@ export const signup = asyncErrorHandler(async (req, res, next) => { } const isDuplicate = await UserService.checkUserExists({ email: email }); - console.log(isDuplicate); if (isDuplicate) { const error = new CustomError( @@ -163,7 +162,7 @@ export const signup = asyncErrorHandler(async (req, res, next) => { return next(error); } - await UserService.createNewUser(name, email); + await UserService.createNewUser(name, email, role_id); res.status(StatusCode.CREATED).json({ status: "success", @@ -171,6 +170,7 @@ export const signup = asyncErrorHandler(async (req, res, next) => { data: { name, email, + role_id, }, }); }); @@ -261,6 +261,53 @@ export const login = asyncErrorHandler(async (req, res, next) => { } }); +/** + * @description Adds user list to users database. + * @route POST /add-members + * @param req - The HTTP request object. + * @param res - The HTTP response object. + * @param next - The next middleware function in the stack. + * @returns A JSON response containing the user database. + * @returns status - Indicates the success status of the operation ('success'). + * @returns message - Describes the outcome of the operation ('User Data successfully inserted'). + * @returns data - Contains the data added to the users database. + * @howItWorks + * - Retrieves the array of users from `req.params.users`. + * - Checks for any duplicate email and returns the list of duplicate email found + * - Hashes all the password being added. + * - adds all the data to users models + */ + +export const postBulkUserController = asyncErrorHandler( + async (req, res, next) => { + const usersData = req.body.users; + if (!Array.isArray(usersData)) { + return res.status(400).json({ error: "User data must be an array" }); + } + + const { createdUsers, existingUsers } = + await UserService.createNewUsers(usersData); + + if (existingUsers.length > 0) { + return res.status(StatusCode.CONFLICT).json({ + message: + createdUsers.length > 0 + ? "Some users were added, but some already exist." + : "All provided users already exist.", + data: { + createdUsers, + existingUsers, + }, + }); + } + + res.status(StatusCode.OK).json({ + message: "User Data inserted successfully", + data: createdUsers, + }); + }, +); + /** * Handle user password reset request. Send a mail to user with password reset link. */ diff --git a/src/api/controllers/eventController.ts b/src/api/controllers/eventController.ts index 119183c..29fc3d8 100644 --- a/src/api/controllers/eventController.ts +++ b/src/api/controllers/eventController.ts @@ -3,17 +3,15 @@ import asyncErrorHandler from "../helpers/asyncErrorHandler"; import StatusCode from "@static/types/backend/httpStatusCode"; import { Event, IEvent } from "../models/eventModel"; -export const getEventsController = asyncErrorHandler( - async (req, res, next) => { - const events = await Event.find().sort(); - - res.status(StatusCode.OK).json({ - status: "success", - message: "Events fetched successfully", - data: events, - }); - }, -); +export const getEventsController = asyncErrorHandler(async (req, res, next) => { + const events = await Event.find().sort(); + + res.status(StatusCode.OK).json({ + status: "success", + message: "Events fetched successfully", + data: events, + }); +}); export const createEventController = asyncErrorHandler( async (req, res, next) => { diff --git a/src/api/controllers/userController.ts b/src/api/controllers/userController.ts index b9e8953..ba205b1 100644 --- a/src/api/controllers/userController.ts +++ b/src/api/controllers/userController.ts @@ -154,6 +154,46 @@ export const getUserController = asyncErrorHandler(async (req, res, next) => { }); }); +/** + * @description Adds user list to users database. + * @route POST /post-add-users + * @param req - The HTTP request object. + * @param res - The HTTP response object. + * @param next - The next middleware function in the stack. + * @returns A JSON response containing the user database. + * @returns status - Indicates the success status of the operation ('success'). + * @returns message - Describes the outcome of the operation ('User Data successfully inserted'). + * @returns data - Contains the data added to the users database. + * @howItWorks + * - Retrieves the array of users from `req.params.users`. + * - Hashes all the password being added. + * - adds all the data to users models + */ + +export const postBulkUserController = asyncErrorHandler( + async (req, res, next) => { + const usersData = req.body.users; + + if (!Array.isArray(usersData)) { + return res.status(400).json({ error: "user data must be an array" }); + } + + const hashedUsers = await Promise.all( + usersData.map(async (user) => ({ + ...user, + password: await bcrypt.hash(user.password, 10), + })), + ); + + const userDocsInserted = await User.insertMany(hashedUsers); + + res.status(StatusCode.OK).json({ + message: "User Data successfully inserted", + data: userDocsInserted, + }); + }, +); + /** * @description Updates user details including name, email, password, and bio. * @route PUT /update-user diff --git a/src/api/routes/authRouter.ts b/src/api/routes/authRouter.ts index 3bed171..1d13055 100644 --- a/src/api/routes/authRouter.ts +++ b/src/api/routes/authRouter.ts @@ -6,6 +6,7 @@ import { forgotPassword, resetPassword, logout, + postBulkUserController, } from "../controllers/authController"; import protected_route from "../middlewares/permsMiddlewareInit"; import Permission from "@static/types/permissions"; @@ -17,6 +18,9 @@ const signup_protect = protected_route([Permission.CreateProfile]); router.route("/signup").post(protect, signup_protect, signup); router.post("/login", login); +router + .route("/add-members") + .post(protect, signup_protect, postBulkUserController); router.get("/refresh", refresh); router.post("/logout", logout); router.post("/forgotPassword", forgotPassword); diff --git a/src/api/routes/userRoute.ts b/src/api/routes/userRoute.ts index ec56151..44ba5bc 100644 --- a/src/api/routes/userRoute.ts +++ b/src/api/routes/userRoute.ts @@ -7,6 +7,7 @@ import { permsUpdateController, updateUserController, deleteUserController, + postBulkUserController, } from "../controllers/userController"; import { protect } from "../middlewares/authMiddleware"; import protected_route from "../middlewares/permsMiddlewareInit"; @@ -14,6 +15,7 @@ import Permission from "@static/types/permissions"; const router = express.Router(); +const createBulkProfileProtect = protected_route([Permission.CreateProfile]); const updateProfileProtect = protected_route([Permission.UpdateProfile]); const deleteProfileProtect = protected_route([Permission.DeleteProfile]); @@ -25,6 +27,10 @@ router.route("/current-user").get(protect, getCurrentUserController); router.route("/get-user/:id").get(protect, getUserController); +router + .route("/post-add-users") + .post(protect, createBulkProfileProtect, postBulkUserController); + router .route("/update-user") .put( diff --git a/src/api/services/userService.ts b/src/api/services/userService.ts index 69fb538..bf9ff8e 100644 --- a/src/api/services/userService.ts +++ b/src/api/services/userService.ts @@ -65,7 +65,11 @@ export const getAllUsers = async ( return allUsers; }; -export const createNewUser = async (name: string, email: string) => { +export const createNewUser = async ( + name: string, + email: string, + role_id: string, +) => { const password: string = generateRandomPassword(7); const hashed_password: string = await bcrypt.hash(password, 10); @@ -73,6 +77,7 @@ export const createNewUser = async (name: string, email: string) => { name: name, email: email, password: hashed_password, + role_id: role_id, }); const reg_mail = new RegisterationMail(user, password); @@ -81,3 +86,28 @@ export const createNewUser = async (name: string, email: string) => { return user; }; + +export const createNewUsers = async (users: Array) => { + const createdUsers: HydratedDocument[] = []; + const incomingEmails = users.map((user) => user.email); + const existingUsers = await User.find({ email: { $in: incomingEmails } }); + const existingEmails = existingUsers.map((user) => user.email); + + console.log(existingEmails); + + for (const user of users) { + if (existingEmails.includes(user.email)) { + continue; + } + const password: string = generateRandomPassword(7); + const hashed_password: string = await bcrypt.hash(password, 10); + const newUser = await User.create({ + ...user, + password: hashed_password, + }); + const reg_mail = new RegisterationMail(newUser, password); + await reg_mail.sendTo(newUser.email); + createdUsers.push(newUser); + } + return { createdUsers, existingUsers }; +}; diff --git a/tsconfig.json b/tsconfig.json index 6747728..9672589 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,35 +1,23 @@ { - "compilerOptions": { - "module": "NodeNext", - "moduleResolution": "nodenext", - "target": "ESNext", - "sourceMap": false, - "outDir": "dist", - "allowJs": false, - "strictNullChecks": true, - "typeRoots": [ - "./src/types", - "./node_modules/@types" - ], - "baseUrl": ".", - "paths": { // Adjust based on your project structure - "@/*": [ - "./src/*" - ], - "@static/*": [ - "./src/commonlib/*" - ] - }, - "resolveJsonModule": true + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "nodenext", + "target": "ESNext", + "sourceMap": false, + "outDir": "dist", + "allowJs": false, + "strictNullChecks": true, + "typeRoots": ["./src/types", "./node_modules/@types"], + "baseUrl": ".", + "paths": { + // Adjust based on your project structure + "@/*": ["./src/*"], + "@static/*": ["./src/commonlib/*"] }, - "ts-node": { - "require": [ - "tsconfig-paths/register" - ] - }, - "exclude": [ - "node_modules", - "dist", - "src/scripts" - ], -} \ No newline at end of file + "resolveJsonModule": true + }, + "ts-node": { + "require": ["tsconfig-paths/register"] + }, + "exclude": ["node_modules", "dist", "src/scripts"] +}