Merge pull request #23 from bluewave-labs/feat/monitors

Feat/monitors
This commit is contained in:
Veysel authored and GitHub committed 2024-05-13 18:24:57 -04:00
commit c51224ebc7
9 files changed
+400 -43

No files matched your search

+92
View File
@@ -28,11 +28,103 @@ BlueWave uptime monitoring application
1. Change directory to the `Server` directory
2. Install all depencies by running `npm install`
---
#### Configuration
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 |
---
#### Starting the Development Server
1. run `npm run dev` to start the development server
---
#### Endpoints
All endpoints return a response in this format:
| Name | Type | Notes |
| ------- | --------- | ----------------------------- |
| success | `boolean` | Success or failure of request |
| msg | `string` | Message describing response |
| data | `Object` | Arbitrary Payload |
Example:
```
{success: true, msg: "Successful Request", data: {test: testData}}
```
##### Data Types
###### Monitor
| Name | Type | Notes |
| ----------- | --------- | ---------------------------------------- |
| userId | `string` | Unique ID identifying monitor creator |
| name | `string` | Name of the monitor |
| description | `string` | Description of the monitor |
| url | `string` | Url the monitor will ping |
| isActive | `boolean` | Whether or not the monitor is active |
| interval | `integer` | Interval with which to ping monitor (ms) |
| 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 |
---
##### GET /api/v1/monitor/:id
###### Response
| Status Code | Type | Description |
| ----------- | --------------------- | ------------------------------ |
| 200 | Response with Payload | Response with a single monitor |
###### Payload
| Type | Notes |
| --------- | --------------------------------------------------- |
| `Monitor` | Single monitor with the id in the request parameter |
---
##### GET /api/v1/monitors/user/:userId
| Status Code | Type | Description |
| ----------- | --------------------- | -------------------------------- |
| 200 | Response with Payload | Response with a list of monitors |
###### Payload
| Type | Notes |
| ---------------- | ------------------------------------------------------------------ |
| `Array[Monitor]` | Array of monitors created by user with userId specified in request |
## Contributors
<a href="https://github.com/bluewave-labs/bluewave-uptime/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/bluewave-uptime" />
</a>
+13 -16
View File
@@ -1,18 +1,15 @@
const mongoose = require('mongoose')
const PORT = process.env.PORT || 5000;
const PORT = process.env.PORT || 5000
const connectDbAndRunServer = async (app, db) => {
try {
await db.connect();
app.listen(PORT, () => {
console.log(`server started on port:${PORT}`);
});
} catch (error) {
console.log("Failed to connect to DB");
console.error(error);
}
};
const connectDbAndRunServer = async (app) => {
await mongoose.connect(process.env.DB_CONNECTION_STRING).then(() => {
console.log("DB connected")
// run server
app.listen(PORT, () => {
console.log(`server started on port:${PORT}`)
})
}).catch((err) => {
console.log(err)
})
}
module.exports = { connectDbAndRunServer }
module.exports = { connectDbAndRunServer };
+56
View File
@@ -0,0 +1,56 @@
const {
getMonitorsByIdValidation,
getMonitorsByUserIdValidation,
} = require("../validation/joi");
// Gets all monitors
const getAllMonitors = async (req, res) => {
try {
const monitors = await req.db.getAllMonitors();
return res.json({ success: true, msg: "Monitors found", data: monitors });
} catch (error) {
return res.status(500).json({ success: false, msg: error.message });
}
};
// Get a monitor by ID
const getMonitorById = async (req, res) => {
const { error } = getMonitorsByIdValidation.validate(req.params);
if (error) {
return res
.status(400)
.json({ success: false, msg: error.details[0].message });
}
try {
const monitorId = req.params.monitorId;
const monitor = await req.db.getMonitorById(monitorId);
return res.json({ success: true, msg: "Monitor found", data: monitor });
} catch (error) {
return res.status(500).json({ success: false, msg: error.message });
}
};
// Gets a monitor by user ID
const getMonitorsByUserId = async (req, res) => {
const { error } = getMonitorsByUserIdValidation.validate(req.params);
if (error) {
return res
.status(400)
.json({ success: false, msg: error.details[0].message });
}
try {
const userId = req.params.userId;
const monitors = await req.db.getMonitorsByUserId(userId);
return res.json({
success: true,
msg: `Monitors for user ${userId} found`,
data: monitors,
});
} catch (error) {
return res.status(500).json({ success: false, msg: error.message });
}
};
module.exports = { getAllMonitors, getMonitorById, getMonitorsByUserId };
+81
View File
@@ -0,0 +1,81 @@
// **************************
// The idea here is to provide a layer of abstraction between the database and whoever is using it.
// Instead of directly calling mongoose methods, we can call the methods on the DB object.
// If this were Typescript or Java or Golang an interface would be implemented to ensure the methods are available.
// But we do the best we can with Javascript.
//
// If the methods are consistent all we have to do to swap out one DB for another is simply change the import.
//
// Example:
// We start with the fake DB:
//
// const db = require("../db/FakeDb");
// const monitors = await db.getAllMonitors();
//
// And when we want to swtich to a real DB, all we have to do is swap the import
//
// const db = require("../db/MongoDb");
// const monitors = await db.getAllMonitors();
//
// The rest of the code is the same, as all the `db` methods are standardized.
// **************************
const Monitor = require("../models/Monitor");
const FAKE_MONITOR_DATA = [];
for (let i = 0; i < 10; i++) {
FAKE_MONITOR_DATA.push(
new Monitor({
userId: i % 2 === 0 ? 1 : 2,
name: `Monitor ${i}`,
description: `Description for Monitor ${i}`,
url: `https://monitor${i}.com`,
isActive: true,
interval: 60000,
updated_at: new Date(),
created_at: new Date(),
})
);
}
const connect = async () => {
try {
await console.log("Connected to FakeDB");
} catch (error) {
console.error(error);
}
};
const getAllMonitors = async () => {
return FAKE_MONITOR_DATA;
};
const getMonitorById = async (monitorId) => {
const idx = FAKE_MONITOR_DATA.findIndex((monitor) => {
return monitor.id === monitorId;
});
if (idx === -1) {
throw new Error(`Monitor with id ${monitorId} not found`);
}
return FAKE_MONITOR_DATA[idx];
};
const getMonitorsByUserId = async (userId) => {
const userMonitors = FAKE_MONITOR_DATA.filter((monitor) => {
return monitor.userId === userId;
});
if (userMonitors.length === 0) {
throw new Error(`Monitors for user ${userId} not found`);
}
return userMonitors;
};
module.exports = {
connect,
getAllMonitors,
getMonitorById,
getMonitorsByUserId,
};
+49
View File
@@ -0,0 +1,49 @@
const Monitor = require("../models/Monitor");
const mongoose = require("mongoose");
const connect = async () => {
try {
await mongoose.connect(process.env.DB_CONNECTION_STRING);
console.log("Connected to MongoDB");
} catch (error) {
console.error("Failed to connect to MongoDB");
throw error;
}
};
// Gets all monitors
const getAllMonitors = async (req, res) => {
try {
const monitors = await Monitor.find();
return monitors;
} catch (error) {
throw error;
}
};
// Get a monitor by ID
const getMonitorById = async (req, res) => {
try {
const monitor = await Monitor.findById(req.params.monitorId);
return monitor;
} catch (error) {
throw error;
}
};
// Gets a monitor by user ID
const getMonitorsByUserId = async (req, res) => {
try {
const monitors = await Monitor.find({ userId: req.params.userId });
return monitors;
} catch (error) {
throw error;
}
};
module.exports = {
connect,
getAllMonitors,
getMonitorById,
getMonitorsByUserId,
};
+49 -22
View File
@@ -1,28 +1,55 @@
const express = require('express')
const helmet = require('helmet')
const cors = require('cors')
const authRouter = require('./routes/authRoute')
const { connectDbAndRunServer } = require('./configs/db')
require('dotenv').config()
const express = require("express");
const helmet = require("helmet");
const cors = require("cors");
const authRouter = require("./routes/authRoute");
const monitorRouter = require("./routes/monitorRoute");
const { connectDbAndRunServer } = require("./configs/db");
require("dotenv").config();
// const { sendEmail } = require('./utils/sendEmail')
// **************************
// Here is where we can swap out DBs easily. Spin up a mongoDB instance and try it out.
// Simply comment out the FakeDB and uncomment the MongoDB or vice versa.
// We can easily swap between any type of data source as long as the methods are implemented
//
// FakeDB
// const db = require("./db/FakeDb");
//
// MongoDB
const db = require("./db/MongoDB");
//
// **************************
/**
* NOTES
* Email Service will be added
* Email Service will be added
* Logger Service will be added (Winston or similar)
*/
const app = express()
const app = express();
// middlewares
app.use(cors(
//We will add configuration later
))
app.use(express.json())
app.use(helmet())
app.use(
cors()
//We will add configuration later
);
app.use(express.json());
app.use(helmet());
// **************************
// Make DB accessible anywhere we have a Request object
// By adding the DB to the request object, we can access it in any route
// Thus we do not need to import it in every route file, and we can easily swap out DBs as there is only one place to change it
// **************************
app.use((req, res, next) => {
req.db = db;
next();
});
//routes
app.use('/api/v1/auth', authRouter);
app.use("/api/v1/auth", authRouter);
app.use("/api/v1/monitors", monitorRouter);
// Testing email service
// app.use('/sendEmail', async (req, res) => {
@@ -31,12 +58,12 @@ app.use('/api/v1/auth', authRouter);
// })
//health check
app.use('/api/v1/healthy', (req, res) => {
try {
return res.status(200).json({message:"Healthy"})
} catch (error) {
return res.status(500).json({message:error.message})
}
})
app.use("/api/v1/healthy", (req, res) => {
try {
return res.status(200).json({ message: "Healthy" });
} catch (error) {
return res.status(500).json({ message: error.message });
}
});
connectDbAndRunServer(app);
connectDbAndRunServer(app, db);
+37
View File
@@ -0,0 +1,37 @@
const mongoose = require("mongoose");
const MonitorSchema = mongoose.Schema({
userId: {
type: String,
},
name: {
type: String,
required: true,
},
description: {
type: String,
},
url: {
type: String,
required: true,
},
isActive: {
type: Boolean,
default: true,
},
interval: {
// in milliseconds
type: Number,
default: 60000,
},
updated_at: {
type: Date,
default: Date.now,
},
created_at: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model("Monitor", MonitorSchema);
+7
View File
@@ -0,0 +1,7 @@
const router = require("express").Router();
const monitorController = require("../controllers/monitorController");
router.get("/", monitorController.getAllMonitors);
router.get("/:monitorId", monitorController.getMonitorById);
router.get("/user/:userId", monitorController.getMonitorsByUserId);
module.exports = router;
+16 -5
View File
@@ -1,10 +1,21 @@
const joi = require('joi')
const joi = require("joi");
const user = require("../models/user");
const authValidation = joi.object({
email: joi.string().email().required(),
password: joi.string().min(8).required(),
email: joi.string().email().required(),
password: joi.string().min(8).required(),
});
const getMonitorsByIdValidation = joi.object({
monitorId: joi.string().required(),
});
module.exports = {authValidation}
const getMonitorsByUserIdValidation = joi.object({
userId: joi.string().required(),
});
module.exports = {
authValidation,
getMonitorsByIdValidation,
getMonitorsByUserIdValidation,
};