mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-12 20:48:43 -05:00
Add files via upload
This commit is contained in:
committed by
GitHub
parent
3a94abb649
commit
12c31865f7
@@ -0,0 +1,15 @@
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { connectDbAndRunServer };
|
||||
@@ -0,0 +1,50 @@
|
||||
const express = require("express");
|
||||
const UserModel = require("../models/user");
|
||||
const { registerValidation } = require("../validation/joi");
|
||||
const logger = require('../utils/logger')
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {express.Request} req
|
||||
* @param {express.Response} res
|
||||
* @returns {{success: Boolean, msg: String}}
|
||||
*/
|
||||
const registerController = async (req, res) => {
|
||||
// joi validation
|
||||
try {
|
||||
await registerValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ success: false, msg: error.details[0].message });
|
||||
}
|
||||
|
||||
// Check if the user exists
|
||||
try {
|
||||
const isUser = await req.db.getUserByEmail(req, res);
|
||||
if (isUser) {
|
||||
logger.warning("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" });
|
||||
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 });
|
||||
} catch (error) {
|
||||
logger.error(error.message, { "service": "auth" });
|
||||
return res.status(500).json({ success: false, msg: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { registerController };
|
||||
@@ -0,0 +1,61 @@
|
||||
const {
|
||||
getMonitorsByIdValidation,
|
||||
getMonitorsByUserIdValidation,
|
||||
} = require("../validation/joi");
|
||||
const logger = require('../utils/logger')
|
||||
|
||||
// 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) {
|
||||
logger.error(error.message, { "service": "monitor" });
|
||||
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) {
|
||||
logger.error(error.message, { "service": "monitor" });
|
||||
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);
|
||||
logger.info(`Monitors for user ${userId} found`, { "service": "monitor", "userId":userId });
|
||||
return res.json({
|
||||
success: true,
|
||||
msg: `Monitors for user ${userId} found`,
|
||||
data: monitors,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error.message, { "service": "monitor" });
|
||||
return res.status(500).json({ success: false, msg: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { getAllMonitors, getMonitorById, getMonitorsByUserId };
|
||||
@@ -0,0 +1,110 @@
|
||||
// **************************
|
||||
// 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 UserModel = require("../models/user");
|
||||
|
||||
const FAKE_MONITOR_DATA = [];
|
||||
const USERS = [];
|
||||
|
||||
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 insertUser = async (req, res) => {
|
||||
try {
|
||||
const newUser = new UserModel({ ...req.body });
|
||||
USERS.push(newUser);
|
||||
return newUser;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getUserByEmail = async (req, res) => {
|
||||
const email = req.body.email;
|
||||
try {
|
||||
const idx = USERS.findIndex((user) => {
|
||||
return user.email === email;
|
||||
});
|
||||
if (idx === -1) {
|
||||
return null;
|
||||
}
|
||||
return USERS[idx];
|
||||
} catch (error) {
|
||||
throw new Error(`User with email ${email} not found`);
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
insertUser,
|
||||
getUserByEmail,
|
||||
getAllMonitors,
|
||||
getMonitorById,
|
||||
getMonitorsByUserId,
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
const Monitor = require("../models/Monitor");
|
||||
const mongoose = require("mongoose");
|
||||
const UserModel = require("../models/user");
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
const insertUser = async (req, res) => {
|
||||
try {
|
||||
const newUser = await UserModel.create({ ...req.body }).select('-password');
|
||||
return newUser;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
} catch (error) {
|
||||
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,
|
||||
insertUser,
|
||||
getUserByEmail,
|
||||
getAllMonitors,
|
||||
getMonitorById,
|
||||
getMonitorsByUserId,
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
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 logger = require("./utils/logger");
|
||||
// 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
|
||||
* Logger Service will be added (Winston or similar)
|
||||
*/
|
||||
|
||||
const app = express();
|
||||
|
||||
// middlewares
|
||||
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/monitors", monitorRouter);
|
||||
|
||||
// Testing email service
|
||||
// app.use('/sendEmail', async (req, res) => {
|
||||
// const response = sendEmail(['veysel.boybay@bluewavelabs.ca'], 'Testing email service', '<h1>Testing Bluewavelabs</h1>');
|
||||
// console.log(response);
|
||||
// })
|
||||
|
||||
//health check
|
||||
app.use("/api/v1/healthy", (req, res) => {
|
||||
try {
|
||||
logger.info("Checking Health of the server.");
|
||||
return res.status(200).json({ message: "Healthy" });
|
||||
} catch (error) {
|
||||
logger.error(error.message);
|
||||
return res.status(500).json({ message: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
connectDbAndRunServer(app, db);
|
||||
@@ -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);
|
||||
@@ -0,0 +1,51 @@
|
||||
const mongoose = require("mongoose");
|
||||
const bcrypt = require("bcrypt");
|
||||
|
||||
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,
|
||||
},
|
||||
updated_at: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
created_at: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
});
|
||||
|
||||
UserSchema.pre("save", async function (next) {
|
||||
if (!this.isModified("password")) {
|
||||
next();
|
||||
}
|
||||
const salt = await bcrypt.genSalt(10); //genSalt is asynchronous, need to wait
|
||||
this.password = bcrypt.hash(this.password, salt);
|
||||
});
|
||||
|
||||
module.exports = mongoose.model("User", UserSchema);
|
||||
Generated
+2127
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "server",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "nodemon index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.1.1",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"helmet": "^7.1.0",
|
||||
"joi": "^17.13.1",
|
||||
"mailersend": "^2.2.0",
|
||||
"mongoose": "^8.3.3",
|
||||
"winston": "^3.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "3.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
const router = require('express').Router();
|
||||
const { registerController } = require('../controllers/authController')
|
||||
|
||||
router.post('/register', registerController)
|
||||
|
||||
module.exports = router;
|
||||
@@ -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;
|
||||
@@ -0,0 +1,26 @@
|
||||
const winston = require('winston');
|
||||
/**
|
||||
* @module
|
||||
* @example
|
||||
* logger.info("Registered a new user!")
|
||||
* logger.warn("User not found!")
|
||||
* logger.error("Cannot save")
|
||||
* @example
|
||||
* "Specify service and ID in the log if applicable."
|
||||
* logger.error("Descriptive Message",{"service":"monitor","monitorId":"123456"})
|
||||
* logger.error("Incorrect Credentials",{"service":"Auth","userId":"654321"})
|
||||
* logger.error("User not found!",{"service":"Auth"})
|
||||
*/
|
||||
const logger = winston.createLogger({
|
||||
level: 'info',
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.json()
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
new winston.transports.File({ filename: 'app.log' }),
|
||||
],
|
||||
});
|
||||
|
||||
module.exports = logger;
|
||||
@@ -0,0 +1,43 @@
|
||||
const { MailerSend, EmailParams, Sender, Recipient } = require('mailersend')
|
||||
const logger = require('../utils/logger')
|
||||
|
||||
|
||||
const mailersend = new MailerSend({
|
||||
apiKey: process.env.MAILERSEND_API_KEY,
|
||||
})
|
||||
/**
|
||||
* @function
|
||||
* @param {[string]} receivers - takes an array of strings
|
||||
* @param {string} subject - takes a single string
|
||||
* @param {string} contentHTML - takes a single string that contains HTML
|
||||
* @returns {JSON}
|
||||
* @example
|
||||
* sendEmail(['veysel@bluewavelabs.ca','alex@bluewavelabs.ca','monzer@bluewavelabs.ca'],'Testing Email Servide','<h1>BlueWaveLabs</h1>')
|
||||
*/
|
||||
// TODO: from email should be in .env file
|
||||
const sendEmail = async (receivers,subject,contentHTML) => {
|
||||
// Sender
|
||||
const from = process.env.SYSTEM_EMAIL_ADDRESS;
|
||||
const sender = new Sender(from);
|
||||
// receivers
|
||||
let recipients = []
|
||||
receivers.map(email => recipients.push(new Recipient(email)));
|
||||
// Set params
|
||||
const emailParams = new EmailParams()
|
||||
.setFrom(sender)
|
||||
.setTo(recipients)
|
||||
.setSubject(subject)
|
||||
.setHtml(contentHTML);
|
||||
|
||||
try {
|
||||
const response = await mailersend.email.send(emailParams);
|
||||
logger.info("Email sent to receivers!",{"service":"Email"})
|
||||
return response;
|
||||
|
||||
} catch (error) {
|
||||
logger.error(error.body,{"service":"email"})
|
||||
console.log(error.body)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {sendEmail}
|
||||
@@ -0,0 +1,28 @@
|
||||
const joi = require("joi");
|
||||
const user = require("../models/user");
|
||||
|
||||
const loginValidation = joi.object({
|
||||
email: joi.string().email().required(),
|
||||
password: joi.string().min(8).required(),
|
||||
});
|
||||
const registerValidation = joi.object({
|
||||
firstname: joi.string().required(),
|
||||
lastname: joi.string().required(),
|
||||
email: joi.string().email().required(),
|
||||
password: joi.string().min(8).required(),
|
||||
});
|
||||
|
||||
const getMonitorsByIdValidation = joi.object({
|
||||
monitorId: joi.string().required(),
|
||||
});
|
||||
|
||||
const getMonitorsByUserIdValidation = joi.object({
|
||||
userId: joi.string().required(),
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
loginValidation,
|
||||
registerValidation,
|
||||
getMonitorsByIdValidation,
|
||||
getMonitorsByUserIdValidation,
|
||||
};
|
||||
Reference in New Issue
Block a user