sensor and location done and some cleanupd

testing will be required
This commit is contained in:
BIG2EYEZ 2024-01-24 15:04:50 +08:00
parent 940d40ed38
commit 5c3a43ddd6
9 changed files with 490 additions and 188 deletions

42
Sean/modules/otpUtils.js Normal file
View File

@ -0,0 +1,42 @@
const nodemailer = require("nodemailer");
const otpGenerator = require('otp-generator');
const path = require('path')
require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
const generateOTP = () => {
const otp = otpGenerator.generate(6, { upperCase: false, specialChars: false });
const expirationTime = Date.now() + 5 * 60 * 1000; // 5 minutes expiration
return { otp, expirationTime };
};
const sendOTPByEmail = async (email, otp) => {
try {
const transporter = nodemailer.createTransport({
service: 'gmail',
host: 'smtp.gmail.com',
port: 587, // use the appropriate port for your SMTP server
secure: false, // true for 465, false for other ports
auth: {
user: process.env.euser, // replace with your email
pass: process.env.epass // replace with your email password
}
});
const mailOptions = {
from: process.env.euser,
to: email,
subject: 'Login OTP',
text: `Your OTP for login is: ${otp}`
};
await transporter.sendMail(mailOptions);
console.log('OTP sent successfully to', email);
} catch (error) {
console.error('Error sending OTP:', error);
throw error;
}
};
module.exports = {
generateOTP,
sendOTPByEmail
};

View File

@ -0,0 +1,9 @@
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 5 requests per windowMs
message: 'Too many login attempts from this IP, please try again later.',
});
module.exports = limiter;

View File

@ -0,0 +1,77 @@
const { body } = require('express-validator');
const locationValidation = [
body('name').trim().isLength({ min: 1 }).withMessage('Name must not be empty').escape(),
body('added_by').trim().isLength({ min: 1 }).withMessage('Added by must not be empty').escape(),
body('description').trim().escape(),
];
const locationValidationUpdate = [
body('id').trim().escape(),
body('name').trim().isLength({ min: 1 }).withMessage('Name must not be empty').escape(),
body('added_by').trim().isLength({ min: 1 }).withMessage('Added by must not be empty').escape(),
body('description').trim().escape(),
];
const locationdeleteValidation = [
body('id').trim().escape()
];
const sensorValidation = [
body('sensorname').trim().isLength({ min: 1 }).withMessage('Sensor Name must not be empty').escape(),
body('added_by').trim().isLength({ min: 1 }).withMessage('Added by must not be empty').escape(),
body('macAddress').custom(value => {
const macAddressRegex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
if (!macAddressRegex.test(value)) {
throw new Error('Invalid MAC address format');
}
return true;
}).withMessage('Invalid MAC address format').escape(),
body('description').trim().escape(),
body('location').trim().escape()
];
const sensorupdateValidation = [
body('id').trim().escape(),
body('sensorname').trim().isLength({ min: 1 }).withMessage('Sensor Name must not be empty').escape(),
body('added_by').trim().isLength({ min: 1 }).withMessage('Added by must not be empty').escape(),
body('macAddress').custom(value => {
const macAddressRegex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
if (!macAddressRegex.test(value)) {
throw new Error('Invalid MAC address format');
}
return true;
}).withMessage('Invalid MAC address format').escape(),
body('description').trim().escape(),
body('location').trim().escape()
];
const sensordeleteValidation = [
body('id').trim().escape()
];
const loginValidation = [
body('username').escape().trim().isLength({ min: 1 }).withMessage('Username must not be empty'),
body('password').escape().trim().isLength({ min: 1 }).withMessage('Password must not be empty'),
];
const otpValidation = [
body('otp').escape().trim().isLength({ min: 1 }).withMessage('OTP must not be empty'),
];
const createValidation = [
body('name').trim().isLength({ min: 1 }).withMessage('Name must not be empty').escape(),
body('username').trim().isLength({ min: 1 }).withMessage('Username must not be empty').escape(),
body('email').isEmail().withMessage('Invalid email address').normalizeEmail(),
body('password').custom((value) => {
if (!isStrongPassword(value)) { throw new Error('Password does not meet complexity requirements'); } return true;
}),
body('jobTitle').trim().isLength({ min: 1 }).withMessage('Job title must not be empty').escape(),
];
module.exports = {
locationValidation,locationValidationUpdate,locationdeleteValidation
,sensorValidation,sensorupdateValidation,sensordeleteValidation,loginValidation,otpValidation
,createValidation
};

View File

@ -1,21 +1,23 @@
const express = require("express"); const express = require("express");
const session = require("express-session"); const session = require("express-session");
const rateLimit = require('express-rate-limit');
const cookieParser = require('cookie-parser'); const cookieParser = require('cookie-parser');
const bodyParser = require("body-parser"); const bodyParser = require("body-parser");
const bcrypt = require("bcrypt"); const bcrypt = require("bcrypt");
const crypto = require("crypto"); const crypto = require("crypto");
const nodemailer = require("nodemailer");
const otpGenerator = require('otp-generator');
const { body, validationResult } = require('express-validator');
const validator = require('validator'); const validator = require('validator');
const axios = require('axios'); const axios = require('axios');
const {validationResult } = require('express-validator');
const {locationValidation, locationValidationUpdate, locationdeleteValidation
,sensorValidation, sensorupdateValidation, sensordeleteValidation, loginValidation
,otpValidation, createValidation} = require('./modules/validationMiddleware');
const rateLimit = require('./modules/rateLimitMiddleware');
const { generateOTP, sendOTPByEmail } = require('./modules/otpUtils');
const { format } = require('date-fns'); const { format } = require('date-fns');
const { Sequelize } = require('sequelize'); const { Sequelize } = require('sequelize');
const { transporter } = require("./modules/nodeMailer"); const { transporter } = require("./modules/nodeMailer");
const { sequelize, User } = require("./modules/mysql"); const { sequelize, User } = require("./modules/mysql");
const userLogs= require('./models/userLogs')(sequelize); // Adjust the path based on your project structure const userLogs= require('./models/userLogs')(sequelize);
const app = express(); const app = express();
app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.urlencoded({ extended: true }));
@ -28,7 +30,6 @@ app.use(bodyParser.urlencoded({ extended: true }));
app.set("view engine", "ejs"); app.set("view engine", "ejs");
app.use(session({ app.use(session({
secret: process.env.key, secret: process.env.key,
resave: false, resave: false,
@ -46,76 +47,27 @@ function isAuthenticated(req, res, next) {
res.redirect("/login"); res.redirect("/login");
} }
} }
const generateOTP = () => {
const otp = otpGenerator.generate(6, { upperCase: false, specialChars: false });
const expirationTime = Date.now() + 5 * 60 * 1000; // 5 minutes expiration
return { otp, expirationTime };
};
const sendOTPByEmail = async (email, otp) => {
try {
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.euser, // replace with your email
pass: process.env.epass // replace with your email password
}
});
const mailOptions = {
from: process.env.euser,
to: email,
subject: 'Login OTP',
text: `Your OTP for login is: ${otp}`
};
await transporter.sendMail(mailOptions);
console.log('OTP sent successfully to', email);
} catch (error) {
console.error('Error sending OTP:', error);
throw error;
}
};
app.get("/login", (req, res) => { app.get("/login", (req, res) => {
res.render("login", { error: null }); res.render("login", { error: null });
}); });
const limiter = rateLimit({ app.use('/login', rateLimit);
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 3 requests per windowMs
message: 'Too many login attempts from this IP, please try again later.',
});
app.use('/login', limiter);
app.post('/login', [
body('username').escape().trim().isLength({ min: 1 }).withMessage('Username must not be empty'),
body('password').escape().trim().isLength({ min: 1 }).withMessage('Password must not be empty'),
],
async (req, res) => {
try {
const errors = validationResult(req);
app.post('/login', loginValidation, async (req, res) => {
try {const errors = validationResult(req);
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
return res.render('login', { error: 'Invalid input. Please check your credentials.', csrfToken: req.session.csrfToken }); return res.render('login', { error: 'Invalid input. Please check your credentials.', csrfToken: req.session.csrfToken });
} }
let { username, password } = req.body; let { username, password } = req.body;
username = username.trim(); username = username.trim();
const user = await User.findOne({ where: { username } }); const user = await User.findOne({ where: { username } });
if (user) { if (user) {
const isLoginSuccessful = await bcrypt.compare(password, user.password); const isLoginSuccessful = await bcrypt.compare(password, user.password);
if (isLoginSuccessful) { if (isLoginSuccessful) {
await userLogs.create({ username, success: true, activity: "Credentials entered correctly" }); await userLogs.create({ username, success: true, activity: "Credentials entered correctly" });
const { otp, expirationTime } = generateOTP(); const { otp, expirationTime } = generateOTP();
req.session.otp = otp; req.session.otp = otp;
@ -161,17 +113,12 @@ async (req, res) => {
// OTP verification route // OTP verification route
app.post("/verify-otp", [ app.post("/verify-otp", otpValidation ,async (req, res) => {
body('otp').escape().trim().isLength({ min: 1 }).withMessage('OTP must not be empty'),
],
async (req, res) => {
try { try {
const errors = validationResult(req); const errors = validationResult(req);
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
return res.render('otp', { error: 'Invalid OTP. Please try again.'}); return res.render('otp', { error: 'Invalid OTP. Please try again.'});
} }
const enteredOTP = req.body.otp; const enteredOTP = req.body.otp;
if (!req.session) { if (!req.session) {
@ -209,17 +156,10 @@ app.post("/verify-otp", [
req.session.authenticated = true; req.session.authenticated = true;
req.session.username = req.body.username; req.session.username = req.body.username;
req.session.sessionToken = sessionToken; req.session.sessionToken = sessionToken;
csrfTokenSession = crypto.randomBytes(32).toString('hex'); csrfTokenSession = crypto.randomBytes(32).toString('hex');
// Log anti-CSRF token
console.log(`Generated Anti-CSRF Token: ${csrfTokenSession}`);
res.cookie('sessionToken', sessionToken, { secure: true, httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000) }); // Expires in 1 day res.cookie('sessionToken', sessionToken, { secure: true, httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000) }); // Expires in 1 day
console.log(`Generated Session Token: ${sessionToken}`);
res.redirect("/home"); res.redirect("/home");
} else { } else {
if (req.body.username) { if (req.body.username) {
@ -262,17 +202,14 @@ app.post("/verify-otp", [
} }
}); });
app.get("/home", isAuthenticated, async (req, res) => {
const response = await axios.get(process.env.API_ALLLOCATION);
app.get("/home", isAuthenticated, (req, res) => { const valueData = response.data;
// Render the home page with sensor data console.log = (valueData);
res.render("home", { res.render("home", { username: req.session.username, valueData});
username: req.session.username,
});
}); });
app.get("/inusers", isAuthenticated, async (req, res) => { app.get("/inusers", isAuthenticated, async (req, res) => {
try { try {
// Fetch all user data from the database using Sequelize // Fetch all user data from the database using Sequelize
@ -318,20 +255,8 @@ function isStrongPassword(password) {
return true; return true;
} }
app.post( app.post(
'/createUser', '/createUser', createValidation, async (req, res) => {
[
body('name').trim().isLength({ min: 1 }).withMessage('Name must not be empty').escape(),
body('username').trim().isLength({ min: 1 }).withMessage('Username must not be empty').escape(),
body('email').isEmail().withMessage('Invalid email address').normalizeEmail(),
body('password').custom((value) => {
if (!isStrongPassword(value)) { throw new Error('Password does not meet complexity requirements'); } return true;
}),
body('jobTitle').trim().isLength({ min: 1 }).withMessage('Job title must not be empty').escape(),
],
async (req, res) => {
try { try {
const errors = validationResult(req); const errors = validationResult(req);
@ -463,24 +388,13 @@ app.post("/forgot-password", async (req, res) => {
const error = "Username or email not found."; const error = "Username or email not found.";
return res.render("forgot-password", { error, success: null }); return res.render("forgot-password", { error, success: null });
} }
// Generate reset token and update the user // Generate reset token and update the user
const reset_token = crypto.randomBytes(20).toString("hex"); const reset_token = crypto.randomBytes(20).toString("hex");
const reset_token_expiry = new Date(Date.now() + 3600000); // Token expires in 1 hour const reset_token_expiry = new Date(Date.now() + 3600000); // Token expires in 1 hour
// Update the user with the reset token and expiry // Update the user with the reset token and expiry
await User.update( await User.update({reset_token,reset_token_expiry,},
{ {where: {id: user.id},}
reset_token,
reset_token_expiry,
},
{
where: {
id: user.id, // Replace 'id' with the actual primary key field of your User model
},
}
); );
// Send email with reset link // Send email with reset link
const resetLink = `http://localhost:3000/reset-password/${reset_token}`; const resetLink = `http://localhost:3000/reset-password/${reset_token}`;
const mailOptions = { const mailOptions = {
@ -488,12 +402,9 @@ app.post("/forgot-password", async (req, res) => {
subject: "Password Reset", subject: "Password Reset",
text: `Click on the following link to reset your password: ${resetLink}`, text: `Click on the following link to reset your password: ${resetLink}`,
}; };
await transporter.sendMail(mailOptions); await transporter.sendMail(mailOptions);
const success = "Password reset email sent successfully. Check your inbox."; const success = "Password reset email sent successfully. Check your inbox.";
res.render("forgot-password", { error: null, success }); res.render("forgot-password", { error: null, success });
// Log the successful sending of the reset link in the database // Log the successful sending of the reset link in the database
await userLogs.create({ await userLogs.create({
username: user.username, username: user.username,
@ -509,29 +420,22 @@ app.post("/forgot-password", async (req, res) => {
console.error("Error during password reset:", error); console.error("Error during password reset:", error);
const errorMessage = "An error occurred during the password reset process."; const errorMessage = "An error occurred during the password reset process.";
res.render("forgot-password", { error: errorMessage, success: null }); res.render("forgot-password", { error: errorMessage, success: null });
} }});
});
app.post("/reset-password/:token", async (req, res) => { app.post("/reset-password/:token", async (req, res) => {
try { try {
const { token } = req.params; const { token } = req.params;
const { password, confirmPassword } = req.body; const { password, confirmPassword } = req.body;
// Sanitize the inputs // Sanitize the inputs
const sanitizedToken = validator.escape(token); const sanitizedToken = validator.escape(token);
const sanitizedPassword = validator.escape(password); const sanitizedPassword = validator.escape(password);
const sanitizedConfirmPassword = validator.escape(confirmPassword); const sanitizedConfirmPassword = validator.escape(confirmPassword);
// Find user with matching reset token and not expired // Find user with matching reset token and not expired
const user = await User.findOne({ const user = await User.findOne({
where: { where: {reset_token: sanitizedToken,
reset_token: sanitizedToken, reset_token_expiry: { [Sequelize.Op.gt]: new Date() },
reset_token_expiry: { [Sequelize.Op.gt]: new Date() },
}, },
}); });
if (!user) { if (!user) {
// Pass the error to the template when rendering the reset-password page // Pass the error to the template when rendering the reset-password page
return res.render("reset-password", { return res.render("reset-password", {
@ -539,7 +443,6 @@ app.post("/forgot-password", async (req, res) => {
resetError: "Invalid or expired reset token", resetError: "Invalid or expired reset token",
}); });
} }
// Check if passwords match // Check if passwords match
if (sanitizedPassword !== sanitizedConfirmPassword) { if (sanitizedPassword !== sanitizedConfirmPassword) {
// Pass the error to the template when rendering the reset-password page // Pass the error to the template when rendering the reset-password page
@ -548,31 +451,24 @@ app.post("/forgot-password", async (req, res) => {
resetError: "Passwords do not match", resetError: "Passwords do not match",
}); });
} }
// Check if the new password meets complexity requirements // Check if the new password meets complexity requirements
if (!isStrongPassword(sanitizedPassword)) { if (!isStrongPassword(sanitizedPassword)) {
// Pass the error to the template when rendering the reset-password page // Pass the error to the template when rendering the reset-password page
return res.render("reset-password", { return res.render("reset-password", {
token, token, resetError:
resetError:
"Password does not meet complexity requirements. It must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.", "Password does not meet complexity requirements. It must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.",
}); });
} }
// Hash the new password // Hash the new password
const saltRounds = 10; const saltRounds = 10;
const hashedPassword = await bcrypt.hash(sanitizedPassword, saltRounds); const hashedPassword = await bcrypt.hash(sanitizedPassword, saltRounds);
// Update user's password and clear reset token // Update user's password and clear reset token
const updateQuery = { const updateQuery = {
password: hashedPassword, password: hashedPassword,
reset_token: null, reset_token: null,
reset_token_expiry: null, reset_token_expiry: null,
}; };
const whereCondition = { const whereCondition = {reset_token: sanitizedToken,};
reset_token: sanitizedToken,
};
await User.update(updateQuery, { await User.update(updateQuery, {
where: whereCondition, where: whereCondition,
}); });
@ -619,10 +515,8 @@ app.post("/reset-password", async (req, res) => {
return res.status(403).json({ error: 'CSRF token mismatch' }); return res.status(403).json({ error: 'CSRF token mismatch' });
} }
const sessionTokencookie = req.cookies['sessionToken']; const sessionTokencookie = req.cookies['sessionToken'];
// Verify sessionToken with the one stored in the database // Verify sessionToken with the one stored in the database
const user = await User.findOne({ where: { sessionid: sessionTokencookie } }); const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
if (!user) { if (!user) {
return res.status(403).json({ error: 'Invalid sessionToken' }); return res.status(403).json({ error: 'Invalid sessionToken' });
} }
@ -630,12 +524,10 @@ app.post("/reset-password", async (req, res) => {
const sanitizedUsername = validator.escape(username); const sanitizedUsername = validator.escape(username);
const sanitizedPassword = validator.escape(password); const sanitizedPassword = validator.escape(password);
const sanitizedConfirmPassword = validator.escape(confirmPassword); const sanitizedConfirmPassword = validator.escape(confirmPassword);
// Check if passwords match // Check if passwords match
if (sanitizedPassword !== sanitizedConfirmPassword) { if (sanitizedPassword !== sanitizedConfirmPassword) {
return res.status(400).json({ error: "Passwords do not match" }); return res.status(400).json({ error: "Passwords do not match" });
} }
// Check if the new password meets complexity requirements // Check if the new password meets complexity requirements
if (!isStrongPassword(sanitizedPassword)) { if (!isStrongPassword(sanitizedPassword)) {
return res.status(400).json({ return res.status(400).json({
@ -643,31 +535,25 @@ app.post("/reset-password", async (req, res) => {
"Password does not meet complexity requirements. It must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.", "Password does not meet complexity requirements. It must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.",
}); });
} }
try { try {
// Find the user in the database // Find the user in the database
const user = await User.findOne({ where: { username: sanitizedUsername } }); const user = await User.findOne({ where: { username: sanitizedUsername } });
if (!user) { if (!user) {
return res.status(404).json({ error: "User does not exist" }); return res.status(404).json({ error: "User does not exist" });
} }
// Generate a random salt and hash the new password // Generate a random salt and hash the new password
const saltRounds = 10; const saltRounds = 10;
const hashedPassword = await bcrypt.hash(sanitizedPassword, saltRounds); const hashedPassword = await bcrypt.hash(sanitizedPassword, saltRounds);
// Update user's password // Update user's password
await User.update( await User.update(
{ password: hashedPassword }, { password: hashedPassword },
{ where: { username: sanitizedUsername } } { where: { username: sanitizedUsername } }
); );
// Log password reset activity // Log password reset activity
await userLogs.create({ await userLogs.create({
username: creatorUsername, username: creatorUsername,
activity: `Password has been reset for ${sanitizedUsername}`, activity: `Password has been reset for ${sanitizedUsername}`,
}); });
// Password update successful // Password update successful
return res.status(200).json({ success: "Password updated successfully" }); return res.status(200).json({ success: "Password updated successfully" });
} catch (error) { } catch (error) {
@ -679,10 +565,8 @@ app.post("/reset-password", async (req, res) => {
app.get('/searchUser', async (req, res) => { app.get('/searchUser', async (req, res) => {
const { username } = req.query; const { username } = req.query;
// Sanitize the input // Sanitize the input
const sanitizedUsername = validator.escape(username); const sanitizedUsername = validator.escape(username);
try { try {
// Find the user in the database // Find the user in the database
const user = await User.findOne({ where: { username: sanitizedUsername } }); const user = await User.findOne({ where: { username: sanitizedUsername } });
@ -690,11 +574,7 @@ app.get('/searchUser', async (req, res) => {
if (!user) { if (!user) {
// No user found with the given username // No user found with the given username
res.status(404).json({ success: false, error: 'User not found' }); res.status(404).json({ success: false, error: 'User not found' });
} else { } else {res.json(user)}
// User found, return user data
res.json(user);
}
} catch (error) { } catch (error) {
console.error('Sequelize query error:', error); console.error('Sequelize query error:', error);
res.status(500).json({ success: false, error: 'Internal Server Error' }); res.status(500).json({ success: false, error: 'Internal Server Error' });
@ -705,7 +585,6 @@ app.get('/api/users', async (req, res) => {
try { try {
// Find all users in the database // Find all users in the database
const users = await User.findAll(); const users = await User.findAll();
// Return the users in the response // Return the users in the response
res.json(users); res.json(users);
} catch (error) { } catch (error) {
@ -774,9 +653,6 @@ app.delete('/api/deleteUser/:username', async (req, res) => {
} }
}); });
app.get('/api/getLogs', async (req, res) => { app.get('/api/getLogs', async (req, res) => {
try { try {
// Query the database to fetch logs using Sequelize model // Query the database to fetch logs using Sequelize model
@ -814,11 +690,6 @@ app.get("/locations", isAuthenticated, async (req, res) => {
} }
}); });
const locationValidation = [
body('name').trim().isLength({ min: 1 }).withMessage('Name must not be empty').escape(),
body('added_by').trim().isLength({ min: 1 }).withMessage('Added by must not be empty').escape(),
body('description').trim().escape(),
];
app.post('/location/new', locationValidation, async (req, res) => { app.post('/location/new', locationValidation, async (req, res) => {
try { try {
const errors = validationResult(req); const errors = validationResult(req);
@ -846,12 +717,6 @@ app.get("/locations", isAuthenticated, async (req, res) => {
} }
}); });
const locationValidationUpdate = [
body('id').trim().escape(),
body('name').trim().isLength({ min: 1 }).withMessage('Name must not be empty').escape(),
body('added_by').trim().isLength({ min: 1 }).withMessage('Added by must not be empty').escape(),
body('description').trim().escape(),
];
app.post('/location/update', locationValidationUpdate, async (req, res) => { app.post('/location/update', locationValidationUpdate, async (req, res) => {
try { try {
const errors = validationResult(req); const errors = validationResult(req);
@ -880,6 +745,33 @@ app.get("/locations", isAuthenticated, async (req, res) => {
}); });
app.post('location/delete',locationdeleteValidation, async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const sessionTokencookie = req.cookies['sessionToken'];
const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
if (!user) {
return res.status(403).json({ error: 'Invalid sessionToken' });
}
const submittedCSRFToken = req.body.csrf_token;
if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) {
return res.status(403).json({ error: 'CSRF token mismatch' });
}
const {id} = req.body;
const preparedData = {id};
// Make a POST request with the sanitized data using Axios
const axiosResponse = await axios.post(process.env.API_DELLOCATION, preparedData);
// Send the Axios response back to the client
res.status(axiosResponse.status).json(axiosResponse.data);
} catch (error) {
console.error('Error handling new sensor submission:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
});
app.get("/sensors", isAuthenticated, async (req, res) => { app.get("/sensors", isAuthenticated, async (req, res) => {
try { try {
// Render the inusers page with JSON data // Render the inusers page with JSON data
@ -894,21 +786,34 @@ app.get("/sensors", isAuthenticated, async (req, res) => {
} }
}); });
const sensorValidation = [
body('id').trim().escape(),
body('sensorname').trim().isLength({ min: 1 }).withMessage('Sensor Name must not be empty').escape(),
body('added_by').trim().isLength({ min: 1 }).withMessage('Added by must not be empty').escape(),
body('macAddress').custom(value => {
const macAddressRegex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
if (!macAddressRegex.test(value)) {
throw new Error('Invalid MAC address format');
}
return true;
}).withMessage('Invalid MAC address format').escape(),
body('description').trim().escape(),
body('location').trim().escape()
];
app.post('sensor/new',sensorValidation, async (req, res) => { app.post('sensor/new',sensorValidation, async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const sessionTokencookie = req.cookies['sessionToken'];
const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
if (!user) {
return res.status(403).json({ error: 'Invalid sessionToken' });
}
const submittedCSRFToken = req.body.csrf_token;
if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) {
return res.status(403).json({ error: 'CSRF token mismatch' });
}
const { sensorname, added_by, macAddress, description, location} = req.body;
const preparedData = {sensorname, added_by, macAddress, description, location};
// Make a POST request with the sanitized data using Axios
const axiosResponse = await axios.post(process.env.API_NEWSENSOR, preparedData);
// Send the Axios response back to the client
res.status(axiosResponse.status).json(axiosResponse.data);
} catch (error) {
console.error('Error handling new sensor submission:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
});
app.post('sensor/update',sensorupdateValidation, async (req, res) => {
try { try {
const errors = validationResult(req); const errors = validationResult(req);
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
@ -935,6 +840,33 @@ const sensorValidation = [
} }
}); });
app.post('sensor/delete',sensordeleteValidation, async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const sessionTokencookie = req.cookies['sessionToken'];
const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
if (!user) {
return res.status(403).json({ error: 'Invalid sessionToken' });
}
const submittedCSRFToken = req.body.csrf_token;
if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) {
return res.status(403).json({ error: 'CSRF token mismatch' });
}
const {id} = req.body;
const preparedData = {id};
// Make a POST request with the sanitized data using Axios
const axiosResponse = await axios.post(process.env.API_DELSENSOR, preparedData);
// Send the Axios response back to the client
res.status(axiosResponse.status).json(axiosResponse.data);
} catch (error) {
console.error('Error handling new sensor submission:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
});
app.use(express.static("views")); app.use(express.static("views"));
app.listen(PORT, () => { app.listen(PORT, () => {

View File

@ -3,19 +3,26 @@ $(document).ready(function () {
$('#locationContainer').show(); $('#locationContainer').show();
$('#createLocationForm').hide(); $('#createLocationForm').hide();
$('#updateLocationForm').hide(); $('#updateLocationForm').hide();
$('#deleteLocationForm').hide();
}); });
$('#addLocationLink').on('click', function () { $('#addLocationLink').on('click', function () {
$('#locationContainer').hide(); $('#locationContainer').hide();
$('#createLocationForm').show(); $('#createLocationForm').show();
$('#updateLocationForm').hide(); $('#updateLocationForm').hide();
$('#deleteLocationForm').hide();
}); });
$('#updateLocationLink').on('click', function () { $('#updateLocationLink').on('click', function () {
$('#locationContainer').hide(); $('#locationContainer').hide();
$('#createLocationForm').hide(); $('#createLocationForm').hide();
$('#updateLocationForm').show(); $('#updateLocationForm').show();
populateLocationDropdown(); $('#deleteLocationForm').hide();
});
$('#deleteLocationLink').on('click', function () {
$('#locationContainer').hide();
$('#createLocationForm').hide();
$('#updateLocationForm').show();
$('#deleteLocationForm').show();
}); });
}); });
let locationArray = []; let locationArray = [];
@ -42,8 +49,7 @@ $(document).ready(function () {
}); });
} }
populateTableAndArray(locationsData); populateTableAndArray(locationsData);
console.log(locationArray); populateLocationDropdown();
$('#locationForm').on('submit', function (e) { $('#locationForm').on('submit', function (e) {
e.preventDefault(); e.preventDefault();
@ -136,3 +142,39 @@ $('#locationForm').on('submit', function (e) {
// Handle error as needed // Handle error as needed
}); });
}); });
$('#deleteForm').on('submit', function (e) {
e.preventDefault();
const selectedLocationId = $('#locationDropdown').val();
const csrf_token = $('#userForm input[name="csrf_token"]').val();
fetch('/location/delete', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id:selectedLocationId,
csrf_token: csrf_token
}),
})
.then(response => {
if (response.ok) {
// Status 201 indicates successful creation
return response.json();
} else {
return response.json().then(data => {
throw new Error(data.message || `HTTP error! Status: ${response.status}`);
});
}
})
.then(data => {
console.log(`Location deleted successfully. Message: ${data.message}`);
alert('Location deleted successfully!');
resetFormFields();
})
.catch(error => {
console.error('Location not deleted successfully', error);
// Handle error as needed
});
});

View File

@ -62,9 +62,9 @@
</div> </div>
<div id="updateLocationForm" class="location-update-container" style="display: none;"> <div id="updateLocationForm" class="location-update-container" style="display: none;">
<h3>Add Location</h3> <h3>Update Location</h3>
<div class="content"> <div class="content">
<form action="/api/v0/location/update" id="updateForm" method="put"> <form action="/location/update" id="updateForm" method="put">
<div class="Location-details"> <div class="Location-details">
<div class="input-box"> <div class="input-box">
<span class="details">Location to Update</span> <span class="details">Location to Update</span>
@ -87,6 +87,25 @@
</form> </form>
</div> </div>
</div> </div>
<div id="deleteLocationForm" class="location-delete-container" style="display: none;">
<h3>Delete Location</h3>
<div class="content">
<form action="/location/delete" id="deleteForm" method="delete">
<div class="Location-details">
<div class="input-box">
<span class="details">Location to Delete</span>
<select name="location" id="locationDropdown" required>
</select>
</div>
</div>
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
<div class="button">
<input type="submit" value="submit">
</div>
</form>
</div>
</div>
<footer> <footer>
Any Issue faced, Please contact the administrator at 11111111 or ecosaverAdmin@gmail.com Any Issue faced, Please contact the administrator at 11111111 or ecosaverAdmin@gmail.com
</footer> </footer>

View File

@ -3,12 +3,32 @@ $(document).ready(function () {
$('#sensorContainer').show(); $('#sensorContainer').show();
$('#createSensorForm').hide(); $('#createSensorForm').hide();
$('#additional-text4').hide(); $('#additional-text4').hide();
$('#updateSensorForm').hide();
$('#deleteSensorForm').hide();
}); });
$('#addSensorLink').on('click', function () { $('#addSensorLink').on('click', function () {
$('#sensorContainer').hide(); $('#sensorContainer').hide();
$('#createSensorForm').show(); $('#createSensorForm').show();
$('#additional-text4').show(); $('#additional-text4').show();
$('#updateSensorForm').hide();
$('#deleteSensorForm').hide();
}); });
$('#updateSensorLink').on('click', function () {
$('#sensorContainer').hide();
$('#createSensorForm').show();
$('#additional-text4').show();
$('#updateSensorForm').show();
$('#deleteSensorForm').hide();
});
$('#deleteSensorLink').on('click', function () {
$('#sensorContainer').hide();
$('#createSensorForm').show();
$('#additional-text4').show();
$('#updateSensorForm').hide();
$('#deleteSensorForm').show();
});
}); });
function populateTableAndArray(data, locationsArray) { function populateTableAndArray(data, locationsArray) {
const tableBody = document.getElementById("sensorTableBody"); const tableBody = document.getElementById("sensorTableBody");
@ -38,16 +58,13 @@ $(document).ready(function () {
function populateLocationDropdown() { function populateLocationDropdown() {
const locationDropdown = document.getElementById('locationDropdown'); const locationDropdown = document.getElementById('locationDropdown');
// Clear existing options // Clear existing options
locationDropdown.innerHTML = ''; locationDropdown.innerHTML = '';
// Add a default option // Add a default option
const defaultOption = document.createElement('option'); const defaultOption = document.createElement('option');
defaultOption.text = 'Select a Location'; defaultOption.text = 'Select a Location';
defaultOption.value = ''; defaultOption.value = '';
locationDropdown.add(defaultOption); locationDropdown.add(defaultOption);
// Add locations as options // Add locations as options
locationsArray.forEach(location => { locationsArray.forEach(location => {
const option = document.createElement('option'); const option = document.createElement('option');
@ -58,6 +75,29 @@ $(document).ready(function () {
} }
populateLocationDropdown(); populateLocationDropdown();
function populateSensorDropdown() {
const sensorDropdown = document.getElementById('sensorDropdown');
// Clear existing options
sensorDropdown.innerHTML = '';
// Add a default option
const defaultOption = document.createElement('option');
defaultOption.text = 'Select a Sensor';
defaultOption.value = '';
sensorDropdown.add(defaultOption);
// Add locations as options
sensorArray.forEach(location => {
const option = document.createElement('option');
option.text = sensor.sensorname;
option.value = sensor.id;
sensorDropdown.add(option);
});
}
populateSensorDropdown();
$('#sensorForm').on('submit', function (e) { $('#sensorForm').on('submit', function (e) {
e.preventDefault(); e.preventDefault();
const sensor = $('#sensor').val(); const sensor = $('#sensor').val();
@ -73,7 +113,6 @@ $('#sensorForm').on('submit', function (e) {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ body: JSON.stringify({
id: id,
sensorname: sensor, sensorname: sensor,
added_by: user, added_by: user,
mac_address: macAddress, mac_address: macAddress,
@ -102,3 +141,85 @@ $('#sensorForm').on('submit', function (e) {
// Handle error as needed // Handle error as needed
}); });
}); });
$('#updatesensorForm').on('submit', function (e) {
e.preventDefault();
const id = $('#sensorDropdown').val();
const sensorname = $('#sensorname').val();
const user = req.session.jobTitle;
const macAddress = $('#macAddress').val();
const description = $('#description').val();
const location = $('#locationDropdown').val();
const csrf_token = $('#userForm input[name="csrf_token"]').val();
fetch('sensor/update', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: id,
sensorname: sensorname,
added_by: user,
mac_address: macAddress,
description: description,
location: location,
csrf_token: csrf_token
}),
})
.then(response => {
if (response.ok) {
// Status 201 indicates successful creation
return response.json();
} else {
return response.json().then(data => {
throw new Error(data.message || `HTTP error! Status: ${response.status}`);
});
}
})
.then(data => {
console.log(`Sensor updated successfully. Message: ${data.message}`);
alert('Sensor updated successfully!');
resetFormFields();
})
.catch(error => {
console.error('Sensor not updated successfully', error);
// Handle error as needed
});
});
$('#deleteForm').on('submit', function (e) {
e.preventDefault();
const selectedSensorId = $('#sensorDropdown').val();
const csrf_token = $('#userForm input[name="csrf_token"]').val();
fetch('/sensor/delete', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id:selectedSensorId,
csrf_token: csrf_token
}),
})
.then(response => {
if (response.ok) {
// Status 201 indicates successful creation
return response.json();
} else {
return response.json().then(data => {
throw new Error(data.message || `HTTP error! Status: ${response.status}`);
});
}
})
.then(data => {
console.log(`Sensor deleted successfully. Message: ${data.message}`);
alert('Sensor deleted successfully!');
resetFormFields();
})
.catch(error => {
console.error('Sensor not deleted successfully', error);
// Handle error as needed
});
});

View File

@ -20,8 +20,8 @@
<nav> <nav>
<a href="#" id="allSensorLink">All Sensor</a> <a href="#" id="allSensorLink">All Sensor</a>
<a href="#" id="addSensorLink">Add Sensor</a> <a href="#" id="addSensorLink">Add Sensor</a>
<a href="#">Update Sensor</a> <a href="#" id="updateSensorLink">Update Sensor</a>
<a href="#">Delete Sensor</a> <a href="#" id="deleteSensorLink">Delete Sensor</a>
<a href="/home" id="homeLink">Home</a> <a href="/home" id="homeLink">Home</a>
</nav> </nav>
@ -83,13 +83,68 @@
</ul> </ul>
</div> </div>
</div> </div>
<div id="updateSensorForm" class="sensor-update-container" style="display: none;">
<h3>Add Location</h3>
<div class="content">
<form action="/sensor/update" id="updateForm" method="put">
<div class="Location-details">
<div class="input-box">
<span class="details">Sensor to Update</span>
<select name="Sensor" id="sensorDropdown" required>
</select>
</div>
<div class="input-box">
<span class="details">Sensor Name</span>
<input type="text" name="sensorname" id="sensorname" placeholder="Enter Sensor name" required>
</div>
<div class="input-box">
<span class="details">Mac Address</span>
<input type="text" name="macAddress" id="macAddress" placeholder="Enter the Mac Address" required>
</div>
<div class="input-box">
<span class="details">Description</span>
<input type="text" name="description" id="description" placeholder="Enter the description here" required>
</div>
</div>
<div class="input-box">
<span class="details">Location</span>
<select name="location" id="locationDropdown" required>
</select>
</div>
</div>
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
<div class="button">
<input type="submit" value="submit">
</div>
</form>
</div>
</div>
<div id="deleteSensorForm" class="sensor-delete-container" style="display: none;">
<h3>Delete Location</h3>
<div class="content">
<form action="/sensor/delete" id="deleteForm" method="delete">
<div class="Location-details">
<div class="input-box">
<span class="details">Sensor to Delete</span>
<select name="sensor" id="sensorDropdown" required>
</select>
</div>
</div>
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
<div class="button">
<input type="submit" value="submit">
</div>
</form>
</div>
</div>
<footer> <footer>
Any Issue faced, Please contact the administrator at 11111111 or ecosaverAdmin@gmail.com Any Issue faced, Please contact the administrator at 11111111 or ecosaverAdmin@gmail.com
</footer> </footer>
</body> </body>
<script> <script>
const locationsArray = <%=-JSON.stringify(locationsData) %>; const locationsArray = <%-JSON.stringify(locationsData) %>;
const sensorArray = <%- JSON.stringify(sensorData) %>; const sensorArray = <%- JSON.stringify(sensorData) %>;
</script> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.3.3/purify.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.3.3/purify.min.js"></script>

5
package-lock.json generated
View File

@ -1626,6 +1626,11 @@
"formdata-polyfill": "^4.0.10" "formdata-polyfill": "^4.0.10"
} }
}, },
"nodemailer": {
"version": "6.9.8",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.8.tgz",
"integrity": "sha512-cfrYUk16e67Ks051i4CntM9kshRYei1/o/Gi8K1d+R34OIs21xdFnW7Pt7EucmVKA0LKtqUGNcjMZ7ehjl49mQ=="
},
"nopt": { "nopt": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",