diff --git a/README.md b/README.md index 039750dc1..8767a3633 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ BlueWave uptime monitoring application 4. [Endpoints](#endpoints) - POST [/api/v1/auth/register](#post-register) - POST [/api/v1/auth/login](#post-login) + - POST [/api/v1/auth/user/{userId}](#post-auth-user-edit-id) - GET [/api/v1/monitors](#get-monitors) - GET [/api/v1/monitor/{id}](#get-monitor-id) - GET [/api/v1/monitors/user/{userId}](#get-monitors-user-userid) @@ -251,6 +252,67 @@ curl --request POST \ --- +
+POST/api/v1/auth/user/{userId} + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +##### Body + +> | Name | Type | Notes | +> | ------------- | -------- | ----------- | +> | firstname | `string` | | +> | lastname | `string` | | +> | profilePicUrl | `string` | | +> | password | `string` | Min 8 chars | + +###### Response Payload + +> | Type | Notes | +> | ------ | ------------------------ | +> | `User` | Returns the updated user | + +##### Sample CURL request + +``` +curl --request POST \ + --url http://localhost:5000/api/v1/auth/user/6654d156634754f789e1f10e \ + --header 'Authorization: ' \ + --header 'Content-Type: application/json' \ + --data '{ + "firstname": "First Name", + "lastname: "Last Name" +}' +``` + +###### Sample Response + +```json +{ + "success": true, + "msg": "User updated", + "data": { + "_id": "6654d156634754f789e1f10e", + "firstname": "First Name", + "lastname": "Last Name", + "email": "me@gmail.com", + "isActive": true, + "isVerified": false, + "createdAt": "2024-05-27T18:30:46.358Z", + "updatedAt": "2024-05-27T19:21:51.747Z", + "__v": 0 + } +} +``` + +
+ +--- +
GET /api/v1/monitors diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index a3a43ba0d..cc476ab54 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -1,5 +1,10 @@ const express = require("express"); -const { registerValidation, loginValidation } = require("../validation/joi"); +const { + registerValidation, + loginValidation, + editUserParamValidation, + editUserBodyValidation, +} = require("../validation/joi"); const logger = require("../utils/logger"); require("dotenv").config(); var jwt = require("jsonwebtoken"); @@ -107,4 +112,36 @@ const loginController = async (req, res, next) => { } }; -module.exports = { registerController, loginController }; +const userEditController = async (req, res, next) => { + try { + await editUserParamValidation.validateAsync(req.params); + await editUserBodyValidation.validateAsync(req.body); + } catch (error) { + error.status = 422; + error.service = SERVICE_NAME; + error.message = error.details[0].message; + next(error); + return; + } + + if (req.params.userId !== req.user._id.toString()) { + const error = new Error("Unauthorized access"); + error.status = 401; + error.service = SERVICE_NAME; + next(error); + return; + } + + try { + const updatedUser = await req.db.updateUser(req, res); + res + .status(200) + .json({ success: true, msg: "User updated", data: updatedUser }); + } catch (error) { + error.service = SERVICE_NAME; + next(error); + return; + } +}; + +module.exports = { registerController, loginController, userEditController }; diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index f7f790e86..a4c9422a3 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -63,6 +63,31 @@ const getUserByEmail = async (req, res) => { // Monitors //**************************************** +/** + * Update a user by ID + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ + +const updateUser = async (req, res) => { + const candidateUserId = req.params.userId; + const candidateUser = req.body; + + try { + const updatedUser = await UserModel.findByIdAndUpdate( + candidateUserId, + candidateUser, + { new: true } // Returns updated user instead of pre-update user + ).select("-password"); + return updatedUser; + } catch (error) { + throw error; + } +}; + /** * Get all monitors * @async @@ -244,6 +269,7 @@ module.exports = { connect, insertUser, getUserByEmail, + updateUser, getAllMonitors, getMonitorById, getMonitorsByUserId, diff --git a/Server/index.js b/Server/index.js index 67813051e..1596e0862 100644 --- a/Server/index.js +++ b/Server/index.js @@ -65,12 +65,6 @@ app.use("/api/v1/auth", authRouter); app.use("/api/v1/monitors", verifyJWT, monitorRouter); app.use("/api/v1/checks", verifyJWT, checkRouter); -/** - * Error handler middleware - * MUST be called after all routes - */ -app.use(handleErrors); - // Testing email service // app.use('/sendEmail', async (req, res) => { // const response = sendEmail(['veysel.boybay@bluewavelabs.ca'], 'Testing email service', '

Testing Bluewavelabs

'); @@ -88,4 +82,10 @@ app.use("/api/v1/healthy", (req, res) => { } }); +/** + * Error handler middleware + * Should be called last + */ +app.use(handleErrors); + connectDbAndRunServer(app, db); diff --git a/Server/middleware/handleErrors.js b/Server/middleware/handleErrors.js index 78764c1e1..a20db8571 100644 --- a/Server/middleware/handleErrors.js +++ b/Server/middleware/handleErrors.js @@ -3,7 +3,7 @@ const logger = require("../utils/logger"); const handleErrors = (error, req, res, next) => { const status = error.status || 500; const message = error.message || "Something went wrong"; - const service = error.errorService || "Unknown service"; + const service = error.service || "Unknown service"; logger.error(error.message, { service: service }); res.status(status).json({ success: false, msg: message }); diff --git a/Server/middleware/verifyOwnership.js b/Server/middleware/verifyOwnership.js index 00f7b0787..409303fa4 100644 --- a/Server/middleware/verifyOwnership.js +++ b/Server/middleware/verifyOwnership.js @@ -19,6 +19,7 @@ const verifyOwnership = (Model, paramName) => { // If the userID does not match the document's userID, return a 403 error if (userId.toString() !== doc.userId.toString()) { + console.log("boom"); const error = new Error("Unauthorized access"); error.status = 403; throw error; diff --git a/Server/models/user.js b/Server/models/user.js index d90b484bb..7f3945354 100644 --- a/Server/models/user.js +++ b/Server/models/user.js @@ -1,44 +1,41 @@ const mongoose = require("mongoose"); const bcrypt = require("bcrypt"); -const UserSchema = mongoose.Schema({ - firstname: { - type: String, - required: true, +const UserSchema = mongoose.Schema( + { + firstname: { + type: String, + required: true, + }, + lastname: { + type: String, + required: true, + }, + email: { + type: String, + required: true, + unique: true, + }, + password: { + type: String, + required: true, + }, + profilePicUrl: { + type: String, + }, + isActive: { + type: Boolean, + default: true, + }, + isVerified: { + type: Boolean, + default: false, + }, }, - lastname: { - type: String, - required: true, - }, - email: { - type: String, - required: true, - unique: true, - }, - password: { - type: String, - required: true, - }, - profilePicUrl: { - type: String, - }, - isActive: { - type: Boolean, - default: true, - }, - isVerified: { - type: Boolean, - default: false, - }, - updated_at: { - type: Date, - default: Date.now, - }, - created_at: { - type: Date, - default: Date.now, - }, -}); + { + timestamps: true, + } +); UserSchema.pre("save", async function (next) { if (!this.isModified("password")) { diff --git a/Server/routes/authRoute.js b/Server/routes/authRoute.js index 48a173b92..637e4bf5e 100644 --- a/Server/routes/authRoute.js +++ b/Server/routes/authRoute.js @@ -1,10 +1,14 @@ const router = require("express").Router(); +const { verifyJWT } = require("../middleware/verifyJWT"); + const { registerController, loginController, + userEditController, } = require("../controllers/authController"); router.post("/register", registerController); router.post("/login", loginController); +router.post("/user/:userId", verifyJWT, userEditController); module.exports = router; diff --git a/Server/validation/joi.js b/Server/validation/joi.js index ecc19253e..399c243dc 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -1,5 +1,4 @@ const joi = require("joi"); -const user = require("../models/user"); const loginValidation = joi.object({ email: joi.string().email().required(), @@ -30,10 +29,22 @@ const monitorValidation = joi.object({ interval: joi.number(), }); +const editUserParamValidation = joi.object({ + userId: joi.string().required(), +}); + +const editUserBodyValidation = joi.object({ + firstname: joi.string(), + lastname: joi.string(), + profilePicUrl: joi.string(), +}); + module.exports = { loginValidation, registerValidation, getMonitorByIdValidation, getMonitorsByUserIdValidation, monitorValidation, + editUserParamValidation, + editUserBodyValidation, };