diff --git a/Sean/modules/otpUtils.js b/Sean/modules/otpUtils.js index 5d6edbd..7ed2780 100644 --- a/Sean/modules/otpUtils.js +++ b/Sean/modules/otpUtils.js @@ -1,12 +1,13 @@ const nodemailer = require("nodemailer"); const otpGenerator = require('otp-generator'); -const path = require('path') +const path = require('path'); require('dotenv').config({ path: path.resolve(__dirname, '../.env') }) const generateOTP = () => { - const otp = otpGenerator.generate(6, { upperCase: false, specialChars: false }); + const otp = otpGenerator.generate(8, { upperCase: true, specialChars: true }); const expirationTime = Date.now() + 5 * 60 * 1000; // 5 minutes expiration return { otp, expirationTime }; + }; const sendOTPByEmail = async (email, otp) => { try { diff --git a/Sean/modules/validationMiddleware.js b/Sean/modules/validationMiddleware.js index 4557937..7a878da 100644 --- a/Sean/modules/validationMiddleware.js +++ b/Sean/modules/validationMiddleware.js @@ -20,7 +20,7 @@ const locationdeleteValidation = [ 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 => { + body('macAddress').escape().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'); @@ -35,7 +35,7 @@ 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 => { + body('macAddress').escape().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'); diff --git a/Sean/server.js b/Sean/server.js index 5c091d4..6215645 100644 --- a/Sean/server.js +++ b/Sean/server.js @@ -77,6 +77,7 @@ app.post('/login', loginValidation, async (req, res) => { req.session.otp = otp; req.session.otpExpiration = expirationTime; req.session.save(); + console.log(otp); try { await sendOTPByEmail(user.email, otp); @@ -160,7 +161,7 @@ app.post("/verify-otp", otpValidation ,async (req, res) => { req.session.authenticated = true; req.session.username = req.body.username; req.session.sessionToken = sessionToken; - csrfTokenSession = crypto.randomBytes(32).toString('hex'); + req.session.csrfToken = crypto.randomBytes(32).toString('hex'); res.cookie('sessionToken', sessionToken, { secure: true, httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000) }); // Expires in 1 day @@ -205,16 +206,27 @@ app.post("/verify-otp", otpValidation ,async (req, res) => { res.status(500).send("Internal Server Error"); } }); + + const getDatabaseStatus = async () => { + try { + await sequelize.authenticate(); + return { connected: true }; + } catch (error) { + console.error('Database connection error:', error); + return { connected: false, error: error.message }; + } + }; + const getSystemHealth = async () => { const cpuInfo = await pidusage(process.pid); + const databaseStatus = await getDatabaseStatus(); return { serverStatus: { uptime: process.uptime() }, - databaseStatus: { connected: true }, // Replace with actual logic + databaseStatus, resourceUtilization: { cpuUsage: cpuInfo.cpu, memoryUsage: process.memoryUsage(), }, - networkHealth: { latency: 10 }, // Replace with actual logic }; }; app.get("/home", isAuthenticated, async (req, res) => { @@ -231,7 +243,7 @@ app.post("/verify-otp", otpValidation ,async (req, res) => { const currentUsername = req.session.username; // Render the inusers page with JSON data - res.render("inusers", {allUsers, csrfToken: csrfTokenSession, currentUsername }); + res.render("inusers", {allUsers, csrfToken: req.session.csrfToken, currentUsername }); } catch (error) { console.error("Error fetching all users:", error); res.status(500).send("Internal Server Error"); @@ -286,8 +298,8 @@ app.post } // Validate the anti-CSRF token const submittedCSRFToken = req.body.csrf_token; - - if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) { + + if (!req.session.csrfToken || submittedCSRFToken !== req.session.csrfToken) { return res.status(403).json({ error: 'CSRF token mismatch' }); } @@ -519,7 +531,7 @@ app.post("/reset-password", async (req, res) => { const creatorUsername = req.session.username; const submittedCSRFToken = req.body.csrf_token; - if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) { + if (!req.session.csrfToken || submittedCSRFToken !== req.session.csrfToken) { return res.status(403).json({ error: 'CSRF token mismatch' }); } const sessionTokencookie = req.cookies['sessionToken']; @@ -632,7 +644,7 @@ app.delete('/api/deleteUser/:username', async (req, res) => { const { csrfToken } = req.body; console.log(csrfToken); // Compare CSRF token with the one stored in the session - if (csrfToken !== csrfTokenSession) { + if (csrfToken !== req.session.csrfToken) { return res.status(403).json({ success: false, error: 'CSRF token mismatch' }); } @@ -691,7 +703,7 @@ app.get("/locations", isAuthenticated, async (req, res) => { const locationsData = response.data; // Render the "locations" page with the fetched JSON data - res.render("locations", { locationsData, csrfToken: csrfTokenSession}); + res.render("locations", { locationsData, csrfToken: req.session.csrfToken}); } catch (error) { console.error("Error fetching locations:", error); res.status(500).send("Internal Server Error"); @@ -710,7 +722,7 @@ app.get("/locations", isAuthenticated, async (req, res) => { return res.status(403).json({ error: 'Invalid sessionToken' }); } const submittedCSRFToken = req.body.csrf_token; - if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) { + if (!req.session.csrfToken || submittedCSRFToken !== req.session.csrfToken) { return res.status(403).json({ error: 'CSRF token mismatch' }); } const { name, added_by, description } = req.body; @@ -737,7 +749,7 @@ app.get("/locations", isAuthenticated, async (req, res) => { return res.status(403).json({ error: 'Invalid sessionToken' }); } const submittedCSRFToken = req.body.csrf_token; - if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) { + if (!req.session.csrfToken || submittedCSRFToken !== req.session.csrfToken) { return res.status(403).json({ error: 'CSRF token mismatch' }); } const { id, name, added_by, description } = req.body; @@ -765,7 +777,7 @@ app.get("/locations", isAuthenticated, async (req, res) => { return res.status(403).json({ error: 'Invalid sessionToken' }); } const submittedCSRFToken = req.body.csrf_token; - if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) { + if (!req.session.csrfToken || submittedCSRFToken !== req.session.csrfToken) { return res.status(403).json({ error: 'CSRF token mismatch' }); } const {id} = req.body; @@ -787,7 +799,7 @@ app.get("/sensors", isAuthenticated, async (req, res) => { const locationsData = response.data; const response2 = await axios.get(process.env.API_ALLSENSOR); const sensorData = response2.data; - res.render("sensors",{locationsData, sensorData, csrfToken: csrfTokenSession}); + res.render("sensors",{locationsData, sensorData, csrfToken: req.session.csrfToken}); } catch (error) { console.error("Error:", error); res.status(500).send("Internal Server Error"); @@ -806,7 +818,7 @@ app.get("/sensors", isAuthenticated, async (req, res) => { return res.status(403).json({ error: 'Invalid sessionToken' }); } const submittedCSRFToken = req.body.csrf_token; - if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) { + if (!req.session.csrfToken || submittedCSRFToken !== req.session.csrfToken) { return res.status(403).json({ error: 'CSRF token mismatch' }); } const { sensorname, added_by, macAddress, description, location} = req.body; @@ -833,7 +845,7 @@ app.get("/sensors", isAuthenticated, async (req, res) => { return res.status(403).json({ error: 'Invalid sessionToken' }); } const submittedCSRFToken = req.body.csrf_token; - if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) { + if (!req.session.csrfToken || submittedCSRFToken !== req.session.csrfToken) { return res.status(403).json({ error: 'CSRF token mismatch' }); } const { id, sensorname, added_by, macAddress, description, location} = req.body; @@ -860,7 +872,7 @@ app.get("/sensors", isAuthenticated, async (req, res) => { return res.status(403).json({ error: 'Invalid sessionToken' }); } const submittedCSRFToken = req.body.csrf_token; - if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) { + if (!req.session.csrfToken || submittedCSRFToken !== req.session.csrfToken) { return res.status(403).json({ error: 'CSRF token mismatch' }); } const {id} = req.body; @@ -875,6 +887,21 @@ app.get("/sensors", isAuthenticated, async (req, res) => { } }); + app.get("/apilog", isAuthenticated, async (req, res) => { + try { + // Fetch data using Axios + const response = await axios.get(process.env.API_LOGS); + const logData = response.data; + + // Render the "locations" page with the fetched JSON data + res.render("locations", {logData}); + } catch (error) { + console.error("Error fetching locations:", error); + res.status(500).send("Internal Server Error"); + } + }); + + app.use(express.static("views")); app.listen(PORT, () => { diff --git a/Sean/views/apilog.ejs b/Sean/views/apilog.ejs new file mode 100644 index 0000000..f5bf9f6 --- /dev/null +++ b/Sean/views/apilog.ejs @@ -0,0 +1,68 @@ + + + + + + + + API Logs + + + + + + +
+

ECOSAVER MANAGEMENT

+
+ + +
+

Welcome to the API Logs Users Page

+
+
+ +
+ + + + + + + + + + + + + <% logData.forEach(entry => { %> + + + + + + + + <% }); %> + +
IDTimeMethodHostDate
<%= entry.id %><%= entry.time %><%= entry.method %><%= entry.host %><%= entry.createdAt %>
+ + + + + + + + + + + + + + + + diff --git a/Sean/views/apilog.js b/Sean/views/apilog.js new file mode 100644 index 0000000..fb23645 --- /dev/null +++ b/Sean/views/apilog.js @@ -0,0 +1,54 @@ +function downloadAsExcel() { + // Get the current date + var currentDate = new Date(); + var formattedDate = currentDate.toISOString().slice(0, 10); // Format as YYYY-MM-DD + + // Create a new workbook + var wb = XLSX.utils.book_new(); + // Convert logData to a worksheet + var ws = XLSX.utils.json_to_sheet(logData); + // Add the worksheet to the workbook + XLSX.utils.book_append_sheet(wb, ws, 'Log Data'); + // Create a blob with the Excel file content + var blob = XLSX.write(wb, { bookType: 'xlsx', type: 'blob', mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); + + // Trigger the download with the filename including the current date + saveAs(blob, 'log_data_' + formattedDate + '.xlsx'); + } + document.addEventListener('DOMContentLoaded', function() { + // Initialize flatpickr on the date input + flatpickr("#datepicker", { + dateFormat: "Y-m-d", + onChange: function(selectedDates, dateStr, instance) { + // Call a function to filter logs based on the selected date + filterLogsByDate(dateStr); + } + }); + + function filterLogsByDate(selectedDate) { + // Use logData to filter logs based on the selected date + var filteredLogs = logData.filter(function(entry) { + return entry.createdAt.startsWith(selectedDate); + }); + + // Render the filtered logs in the table + renderLogsTable(filteredLogs); + } + + function renderLogsTable(logs) { + var tableBody = document.getElementById('logsTableBody'); + tableBody.innerHTML = ''; + + logs.forEach(function(entry) { + var row = document.createElement('tr'); + row.innerHTML = ` + ${entry.id} + ${entry.time} + ${entry.method} + ${entry.host} + ${entry.createdAt} + `; + tableBody.appendChild(row); + }); + } + }); \ No newline at end of file diff --git a/Sean/views/home.ejs b/Sean/views/home.ejs index 65ed9e8..df169f4 100644 --- a/Sean/views/home.ejs +++ b/Sean/views/home.ejs @@ -22,6 +22,7 @@ Users Sensors Locations + Api Logs Logout @@ -57,12 +58,6 @@ -
-

Network Latency

- -