Merge branch 'master' into feat/check-routes

This commit is contained in:
Alex Holliday
2024-05-27 15:38:12 -07:00
9 changed files with 185 additions and 47 deletions
+62
View File
@@ -12,6 +12,7 @@ BlueWave uptime monitoring application
4. [Endpoints](#endpoints)
- <code>POST</code> [/api/v1/auth/register](#post-register)
- <code>POST</code> [/api/v1/auth/login](#post-login)
- <code>POST</code> [/api/v1/auth/user/{userId}](#post-auth-user-edit-id)
- <code>GET</code> [/api/v1/monitors](#get-monitors)
- <code>GET</code> [/api/v1/monitor/{id}](#get-monitor-id)
- <code>GET</code> [/api/v1/monitors/user/{userId}](#get-monitors-user-userid)
@@ -251,6 +252,67 @@ curl --request POST \
---
<details>
<summary id='post-auth-user-edit-id'><code>POST</code><b>/api/v1/auth/user/{userId}</b></summary>
###### 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: <bearer_token>' \
--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
}
}
```
</details>
---
<details>
<summary id='get-monitors'><code>GET</code> <b>/api/v1/monitors</b></summary>
+39 -2
View File
@@ -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 };
+26
View File
@@ -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<UserModel>}
* @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,
+6 -6
View File
@@ -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', '<h1>Testing Bluewavelabs</h1>');
@@ -88,4 +82,10 @@ app.use("/api/v1/healthy", (req, res) => {
}
});
/**
* Error handler middleware
* Should be called last
*/
app.use(handleErrors);
connectDbAndRunServer(app, db);
+1 -1
View File
@@ -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 });
+1
View File
@@ -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;
+34 -37
View File
@@ -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")) {
+4
View File
@@ -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;
+12 -1
View File
@@ -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,
};