mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-26 19:59:12 -06:00
Merge pull request #36 from bluewave-labs/feat/login-route
Added login route, updated readme
This commit is contained in:
235
README.md
235
README.md
@@ -6,6 +6,11 @@ BlueWave uptime monitoring application
|
||||
|
||||
- Clone this repository to your local machine
|
||||
|
||||
1. [Installation (Client)](#client)
|
||||
1. [Installation (Server)](#server)
|
||||
1. [Configuration(Server)](#config-server)
|
||||
1. [Endpoints](#endpoints)
|
||||
|
||||
---
|
||||
|
||||
### Client
|
||||
@@ -30,14 +35,14 @@ BlueWave uptime monitoring application
|
||||
|
||||
---
|
||||
|
||||
#### Configuration
|
||||
#### Configuration {#config-server}
|
||||
|
||||
Configure the server with the following environmental variables
|
||||
|
||||
| ENV Varialbe Name | Required/Optional | Type | Description |
|
||||
| -------------------- | ----------------- | ------ | --------------------------------- |
|
||||
| DB_CONNECTION_STRING | Required | string | Specfies URL for MongoDB Database |
|
||||
| MAILERSEND_API_KEY | Required | string | Specfies API KEY for MailerSend service |
|
||||
| ENV Varialbe Name | Required/Optional | Type | Description |
|
||||
| -------------------- | ----------------- | ------ | ---------------------------------------------------- |
|
||||
| DB_CONNECTION_STRING | Required | string | Specfies URL for MongoDB Database |
|
||||
| MAILERSEND_API_KEY | Required | string | Specfies API KEY for MailerSend service |
|
||||
| SYSTEM_EMAIL_ADDRESS | Required | string | Specfies System email to be used in emailing service |
|
||||
|
||||
---
|
||||
@@ -64,9 +69,28 @@ Example:
|
||||
{success: true, msg: "Successful Request", data: {test: testData}}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
##### Data Types
|
||||
|
||||
###### Monitor
|
||||
<details>
|
||||
<summary><code>User</code></summary>
|
||||
|
||||
| Name | Type | Notes |
|
||||
| ------------- | --------- | --------------------- |
|
||||
| firstname | `string` | First name |
|
||||
| lastname | `string` | Last name |
|
||||
| email | `string` | User's email |
|
||||
| profilePicUrl | `string` | URL to User's picture |
|
||||
| isActive | `boolean` | Default to `true` |
|
||||
| isVerified | `boolean` | Default to `false` |
|
||||
| updated_at | `Date` | Last update time |
|
||||
| created_at | `Date` | Time created at |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><code>Monitor</code></summary>
|
||||
|
||||
| Name | Type | Notes |
|
||||
| ----------- | --------- | ---------------------------------------- |
|
||||
@@ -79,51 +103,186 @@ Example:
|
||||
| updatedAt | `Date` | Last time the monitor was updated |
|
||||
| CreatedAt | `Date` | When the monitor was updated |
|
||||
|
||||
---
|
||||
|
||||
##### GET /api/v1/monitors
|
||||
|
||||
###### Response
|
||||
|
||||
| Status Code | Type | Description |
|
||||
| ----------- | --------------------- | -------------------------------- |
|
||||
| 200 | Response with Payload | Response with a list of monitors |
|
||||
|
||||
###### Payload
|
||||
|
||||
| Type | Notes |
|
||||
| ---------------- | ----------------- |
|
||||
| `Array[Monitor]` | Array of monitors |
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
##### GET /api/v1/monitor/:id
|
||||
<details>
|
||||
<summary><code>POST</code> <b>/api/v1/auth/register</b></summary>
|
||||
|
||||
###### Response
|
||||
##### Method/Headers
|
||||
|
||||
| Status Code | Type | Description |
|
||||
| ----------- | --------------------- | ------------------------------ |
|
||||
| 200 | Response with Payload | Response with a single monitor |
|
||||
> | Method/Headers | Value |
|
||||
> | -------------- | ---------------- |
|
||||
> | Method | POST |
|
||||
> | content-type | application/json |
|
||||
|
||||
###### Payload
|
||||
##### Body
|
||||
|
||||
| Type | Notes |
|
||||
| --------- | --------------------------------------------------- |
|
||||
| `Monitor` | Single monitor with the id in the request parameter |
|
||||
> | Name | Type | Notes |
|
||||
> | --------- | -------- | ------------------- |
|
||||
> | firstname | `string` | |
|
||||
> | lastname | `string` |
|
||||
> | email | `string` | Valid email address |
|
||||
> | password | `string` | Min 8 chars |
|
||||
|
||||
##### Response Payload
|
||||
|
||||
> | Type | Notes |
|
||||
> | ------ | ---------- |
|
||||
> | `User` | User model |
|
||||
|
||||
##### Sample CURL request
|
||||
|
||||
```
|
||||
curl --request POST \
|
||||
--url http://localhost:5000/api/v1/auth/register \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"firstname" : "User First Name",
|
||||
"lastname": "User Last Name",
|
||||
"email" : "user@gmail.com",
|
||||
"password": "user_password"
|
||||
}'
|
||||
```
|
||||
|
||||
##### Sample Response
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"msg": "User created}",
|
||||
"data": {
|
||||
"_id": "6645079aae0b439371913972",
|
||||
"firstname": "User First Name",
|
||||
"lastname": "User Last Name",
|
||||
"email": "user@gmail.com",
|
||||
"isActive": true,
|
||||
"isVerified": false,
|
||||
"updated_at": "2024-05-15T19:06:02.720Z",
|
||||
"created_at": "2024-05-15T19:06:02.720Z",
|
||||
"__v": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
##### GET /api/v1/monitors/user/:userId
|
||||
<details>
|
||||
<summary><code>POST</code> <b>/api/v1/auth/login</b></summary>
|
||||
|
||||
| Status Code | Type | Description |
|
||||
| ----------- | --------------------- | -------------------------------- |
|
||||
| 200 | Response with Payload | Response with a list of monitors |
|
||||
##### Method/Headers
|
||||
|
||||
###### Payload
|
||||
> | Method/Headers | Value |
|
||||
> | -------------- | ---------------- |
|
||||
> | Method | POST |
|
||||
> | content-type | application/json |
|
||||
|
||||
| Type | Notes |
|
||||
| ---------------- | ------------------------------------------------------------------ |
|
||||
| `Array[Monitor]` | Array of monitors created by user with userId specified in request |
|
||||
##### Body
|
||||
|
||||
> | Name | Type | Notes |
|
||||
> | -------- | -------- | ------------------- |
|
||||
> | email | `string` | Valid email address |
|
||||
> | password | `string` | Min 8 chars |
|
||||
|
||||
##### Response Payload
|
||||
|
||||
> | Type | Notes |
|
||||
> | ------ | ---------- |
|
||||
> | `User` | User model |
|
||||
|
||||
##### Sample CURL request
|
||||
|
||||
```
|
||||
curl --request POST \
|
||||
--url http://localhost:5000/api/v1/auth/login \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"email" : "user@gmail.com",
|
||||
"password": "user_password"
|
||||
}'
|
||||
```
|
||||
|
||||
##### Sample response
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"msg": "Found user",
|
||||
"data": {
|
||||
"_id": "6644fb9bae0b439371913969",
|
||||
"firstname": "User First Name",
|
||||
"lastname": "User Last Name",
|
||||
"email": "user@gmail.com",
|
||||
"isActive": true,
|
||||
"isVerified": false,
|
||||
"updated_at": "2024-05-15T18:14:51.049Z",
|
||||
"created_at": "2024-05-15T18:14:51.049Z",
|
||||
"__v": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><code>GET</code> <b>/api/v1/monitors</b></summary>
|
||||
|
||||
##### Method/Headers
|
||||
|
||||
> | Method/Headers | Value |
|
||||
> | -------------- | ----- |
|
||||
> | Method | GET |
|
||||
|
||||
##### Response Payload
|
||||
|
||||
> | Type | Notes |
|
||||
> | ---------------- | ----------------- |
|
||||
> | `Array[Monitor]` | Array of monitors |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><code>GET</code> <b>/api/v1/monitor/{id}</b></summary>
|
||||
|
||||
###### Method/Headers
|
||||
|
||||
> | Method/Headers | Value |
|
||||
> | -------------- | ----- |
|
||||
> | Method | GET |
|
||||
|
||||
###### Response Payload
|
||||
|
||||
> | Type | Notes |
|
||||
> | --------- | --------------------------------------------------- |
|
||||
> | `Monitor` | Single monitor with the id in the request parameter |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><code>GET</code> <b>/api/v1/monitors/user/{userId}</b></summary>
|
||||
|
||||
###### Method/Headers
|
||||
|
||||
> | Method/Headers | Value |
|
||||
> | -------------- | ----- |
|
||||
> | Method | GET |
|
||||
|
||||
###### Request Payload
|
||||
|
||||
> | Type | Notes |
|
||||
> | ---------------- | ------------------------------------------------------------------ |
|
||||
> | `Array[Monitor]` | Array of monitors created by user with userId specified in request |
|
||||
|
||||
</details>
|
||||
|
||||
## Contributors
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const express = require("express");
|
||||
const UserModel = require("../models/user");
|
||||
const { registerValidation } = require("../validation/joi");
|
||||
const logger = require('../utils/logger')
|
||||
const { registerValidation, loginValidation } = require("../validation/joi");
|
||||
const logger = require("../utils/logger");
|
||||
|
||||
/**
|
||||
* @function
|
||||
@@ -23,28 +23,73 @@ const registerController = async (req, res) => {
|
||||
try {
|
||||
const isUser = await req.db.getUserByEmail(req, res);
|
||||
if (isUser) {
|
||||
logger.warning("User already exists!", { "service": "auth", "userId": isUser._id });
|
||||
logger.error("User already exists!", {
|
||||
service: "auth",
|
||||
userId: isUser._id,
|
||||
});
|
||||
return res
|
||||
.status(400)
|
||||
.json({ success: false, msg: "User already exists!" });
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(error.message, { "service": "auth" });
|
||||
logger.error(error.message, { service: "auth" });
|
||||
return res.status(500).json({ success: false, msg: error.message });
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// Create a new user
|
||||
const newUser = await req.db.insertUser(req, res);
|
||||
// TODO: Send an email to user
|
||||
// Will add this later
|
||||
logger.info("New user created!", { "service": "auth", "userId": newUser._id });
|
||||
return res.json({ success: true, msg: "User created}", data: newUser });
|
||||
logger.info("New user created!", { service: "auth", userId: newUser._id });
|
||||
return res
|
||||
.status(200)
|
||||
.json({ success: true, msg: "User created}", data: newUser });
|
||||
} catch (error) {
|
||||
logger.error(error.message, { "service": "auth" });
|
||||
logger.error(error.message, { service: "auth" });
|
||||
return res.status(500).json({ success: false, msg: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { registerController };
|
||||
// **************************
|
||||
// Handles logging in a user
|
||||
// Returns a user at the moment, but will likely return a JWT in the future
|
||||
// **************************
|
||||
const loginController = async (req, res) => {
|
||||
try {
|
||||
// Validate input
|
||||
await loginValidation.validateAsync(req.body);
|
||||
|
||||
// Check if user exists
|
||||
const user = await req.db.getUserByEmail(req, res);
|
||||
|
||||
// If user not found, throw an error
|
||||
if (!user) {
|
||||
throw new Error("User not found!");
|
||||
}
|
||||
// Compare password
|
||||
const match = await user.comparePassword(req.body.password);
|
||||
console.log(user);
|
||||
if (!match) {
|
||||
throw new Error("Password does not match!");
|
||||
}
|
||||
|
||||
// Copy user to remove password. More memory, but better than mutating user?
|
||||
// We can move this to the DB layer if we want
|
||||
// Probably not neccessary at all as we'll likely return a JWT instead of a user
|
||||
const userWithoutPassword = { ...user._doc };
|
||||
delete userWithoutPassword.password;
|
||||
|
||||
// Happy path, return user
|
||||
// In the future we'll probably return a JWT instead of a user
|
||||
return res
|
||||
.status(200)
|
||||
.json({ success: true, msg: "Found user", data: userWithoutPassword });
|
||||
} catch (error) {
|
||||
// Anything else should be an error
|
||||
logger.error(error.message, { service: "auth" });
|
||||
return res.status(500).json({ success: false, msg: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { registerController, loginController };
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
const Monitor = require("../models/Monitor");
|
||||
const UserModel = require("../models/user");
|
||||
const bcrypt = require("bcrypt");
|
||||
|
||||
const FAKE_MONITOR_DATA = [];
|
||||
const USERS = [];
|
||||
@@ -52,6 +53,8 @@ const connect = async () => {
|
||||
const insertUser = async (req, res) => {
|
||||
try {
|
||||
const newUser = new UserModel({ ...req.body });
|
||||
const salt = await bcrypt.genSalt(10); //genSalt is asynchronous, need to wait
|
||||
newUser.password = await bcrypt.hash(newUser.password, salt); // hash is also async, need to eitehr await or use hashSync
|
||||
USERS.push(newUser);
|
||||
return newUser;
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const Monitor = require("../models/Monitor");
|
||||
const mongoose = require("mongoose");
|
||||
const UserModel = require("../models/user");
|
||||
const user = require("../models/user");
|
||||
|
||||
const connect = async () => {
|
||||
try {
|
||||
@@ -14,18 +15,22 @@ const connect = async () => {
|
||||
|
||||
const insertUser = async (req, res) => {
|
||||
try {
|
||||
const newUser = await UserModel.create({ ...req.body }).select('-password');
|
||||
return newUser;
|
||||
const newUser = new UserModel({ ...req.body });
|
||||
await newUser.save();
|
||||
return await UserModel.findOne({ _id: newUser._id }).select("-password"); // .select() doesn't work with create, need to save then find
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// Gets a user by Email. Not sure if we'll ever need this except for login.
|
||||
// If not needed except for login, we can move password comparison here
|
||||
const getUserByEmail = async (req, res) => {
|
||||
try {
|
||||
// Returns null if no user is found
|
||||
const user = await UserModel.findOne({ email: req.body.email }).select('-password');
|
||||
return user;
|
||||
// Need the password to be able to compare, removed .select()
|
||||
// We can strip the hash before returing the user
|
||||
return await UserModel.findOne({ email: req.body.email });
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,13 @@ UserSchema.pre("save", async function (next) {
|
||||
next();
|
||||
}
|
||||
const salt = await bcrypt.genSalt(10); //genSalt is asynchronous, need to wait
|
||||
this.password = bcrypt.hash(this.password, salt);
|
||||
this.password = await bcrypt.hash(this.password, salt); // hash is also async, need to eitehr await or use hashSync
|
||||
next();
|
||||
});
|
||||
|
||||
UserSchema.methods.comparePassword = function (submittedPassword) {
|
||||
console.log(submittedPassword, this.password);
|
||||
return bcrypt.compare(submittedPassword, this.password);
|
||||
};
|
||||
|
||||
module.exports = mongoose.model("User", UserSchema);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
const router = require('express').Router();
|
||||
const { registerController } = require('../controllers/authController')
|
||||
const router = require("express").Router();
|
||||
const {
|
||||
registerController,
|
||||
loginController,
|
||||
} = require("../controllers/authController");
|
||||
|
||||
router.post('/register', registerController)
|
||||
router.post("/register", registerController);
|
||||
router.post("/login", loginController);
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user