From 24fdac91781bf8f782f203aff29b7bab4f1d4bf0 Mon Sep 17 00:00:00 2001 From: Vamshi-Gollapelly Date: Sat, 16 May 2026 11:06:43 +1000 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20Task=207=20=E2=80=94=20shared?= =?UTF-8?q?=20UI=20utilities,=20loading=20states,=20retry=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extended showToast with typed success/error/warning/info notifications - Added withRetry for automatic retry with user-facing toast feedback - Added getApiErrorMessage for consistent user-safe error strings - Added showPageBanner/hidePageBanner for persistent notices - Added confirmDialog, guardRequest, debounce, guardFormSubmit helpers - Removed duplicate getApiErrorMessage from routes.js - Fixed session guard logic in middleware/index.js - Fixed map.routes.js port and removed next() after res.send() crashes - Fixed user.routes.js headers-already-sent crash - Fixed server.js proxyToApi crash on connection failure --- src/Components/HMI/ui/middleware/index.js | 162 +- src/Components/HMI/ui/public/index.html | 3999 +++++++----------- src/Components/HMI/ui/public/js/HMI-utils.js | 780 +++- src/Components/HMI/ui/public/js/HMI.js | 1793 +++----- src/Components/HMI/ui/public/js/map.js | 251 +- src/Components/HMI/ui/public/js/routes.js | 138 +- src/Components/HMI/ui/routes/map.routes.js | 147 +- src/Components/HMI/ui/routes/user.routes.js | 50 +- src/Components/HMI/ui/server.js | 34 +- 9 files changed, 3240 insertions(+), 4114 deletions(-) diff --git a/src/Components/HMI/ui/middleware/index.js b/src/Components/HMI/ui/middleware/index.js index c9066673e..70c546baf 100644 --- a/src/Components/HMI/ui/middleware/index.js +++ b/src/Components/HMI/ui/middleware/index.js @@ -1,41 +1,149 @@ +"use strict"; + +/** + * index.js + * Session middleware and Redis client for the EchoNet backend. + * + * Sprint 1/2 : Redis JWT session check, checkUserSession middleware. + * Task 7 : Fixed inverted / broken route guard logic. + * Removed dead null/undefined checks on req.path. + * Separated public routes (no token needed) from protected routes + * (token required) so the intent is explicit and easy to extend. + * Added clearUserSession() for logout flows. + * Improved Redis error handling and connection guard. + */ + const verifySignUp = require("./verifySignup"); -const redis = require('redis'); +const redis = require("redis"); + +// ───────────────────────────────────────────────────────────────────────────── +// Redis client +// ───────────────────────────────────────────────────────────────────────────── -const redisConfig = { - host: 'echo-redis', - port: 6379, -}; const client = redis.createClient({ socket: { - host: 'echo-redis', - port: 6379 - } + host: "localhost", + port: 6379, + }, +}); + +client.on("error", (err) => { + console.error("Redis client error:", err); }); +/** + * Ensure the Redis client is connected before use. + * Guards against both a closed connection and a not-yet-ready state. + */ +async function ensureRedisConnected() { + if (!client.isOpen) { + await client.connect(); + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Route lists +// +// PUBLIC_ROUTES — accessible without a session token. +// All other routes are treated as protected and require a valid JWT in Redis. +// +// To add a new public route, add its exact path string to PUBLIC_ROUTES or +// its prefix to PUBLIC_PREFIXES below. Do not add it to the middleware +// condition directly — keeping the lists here makes auditing straightforward. +// ───────────────────────────────────────────────────────────────────────────── + +const PUBLIC_ROUTES = new Set(["/login", "/signup", "/map"]); + +/** + * Path prefixes that are always public regardless of the full path. + * e.g. "/admin" covers "/admin", "/admin/users", "/admin/settings". + */ +const PUBLIC_PREFIXES = ["/admin", "/public", "/static"]; + +/** + * Return true if the given path should be accessible without a session token. + * + * @param {string} path - Express req.path value. + * @returns {boolean} + */ +function _isPublicRoute(path) { + if (PUBLIC_ROUTES.has(path)) return true; + return PUBLIC_PREFIXES.some((prefix) => path.startsWith(prefix)); +} + +// ───────────────────────────────────────────────────────────────────────────── +// Session middleware +// ───────────────────────────────────────────────────────────────────────────── + +/** + * Express middleware that checks for a valid JWT stored in Redis. + * + * - Public routes (defined above) pass through without a token check. + * - All other routes require a JWT to be present in Redis under the key "JWT". + * - If no token is found, or Redis errors, the request is redirected to /login. + * + * @param {import('express').Request} req + * @param {import('express').Response} res + * @param {import('express').NextFunction} next + */ async function checkUserSession(req, res, next) { - console.log("Get user session: ", req.path) - if (req.path == '/welcome' || req.path == '/' || req.path == '/map' | req.path.includes("admin") | req.path == null | req.path == undefined) { - - let token = await client.get('JWT', (err, storedToken) => { - if (err) { - console.error('Error retrieving token from Redis:', err); - return null - } else { - console.log('Stored Token:', storedToken); - return storedToken - } - }) - if (token == null) { - // Token is missing, redirect the user to the login page - console.log("No stored token, return to login") - return res.redirect('/login'); + console.log("Session check:", req.path); + + // Public routes skip the token check entirely + if (_isPublicRoute(req.path)) { + return next(); + } + + // Protected route — verify a token exists in Redis + try { + await ensureRedisConnected(); + + const token = await client.get("JWT"); + + if (!token) { + console.log("No stored token — redirecting to login"); + return res.redirect("/login"); } + + return next(); + } catch (error) { + console.error("Session check failed (Redis error):", error); + return res.redirect("/login"); } - return next() } +// ───────────────────────────────────────────────────────────────────────────── +// Session helpers +// ───────────────────────────────────────────────────────────────────────────── + +/** + * Remove the JWT from Redis, effectively logging the user out. + * Call this from your logout route handler. + * + * @returns {Promise} + * + * @example + * app.post("/logout", async (req, res) => { + * await clearUserSession(); + * res.redirect("/login"); + * }); + */ +async function clearUserSession() { + try { + await ensureRedisConnected(); + await client.del("JWT"); + } catch (error) { + console.error("Failed to clear session from Redis:", error); + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Exports +// ───────────────────────────────────────────────────────────────────────────── + module.exports = { verifySignUp, checkUserSession, - client -}; \ No newline at end of file + clearUserSession, + client, +}; diff --git a/src/Components/HMI/ui/public/index.html b/src/Components/HMI/ui/public/index.html index 094b6dfa9..78f9e898a 100644 --- a/src/Components/HMI/ui/public/index.html +++ b/src/Components/HMI/ui/public/index.html @@ -1,143 +1,138 @@ - + + - - - - + - + + + - - - - + + + - - + + - - + + - - - + + + + - - - + + + - + + + - - Echo | Welcome - + -
+ +
- - -
-
-
-
- + +
+
+
+
-
- PROJECT ECHO - Click to start -
-
-
- -
- +
+ PROJECT ECHO + Click to start +
+
+
+ +
+ -
- - +
- + - - +
-
- +
+
+
-

-


+ +


- + -

+


-
+ +

Please enter a link to a reliable source that backs up your claim to make this edit.

- -

Spamming or intentionally abusing this functionality will not be tolerated and will result in your account being either suspended or banned.

+ +

Spamming or intentionally abusing this functionality will not be tolerated and will result in your account being either suspended or banned.