diff --git a/client/src/App.tsx b/client/src/App.tsx
index 6291726..f1e3919 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -1,48 +1,39 @@
-import {
- BrowserRouter as Router,
- Routes,
- Route,
- Navigate,
-} from "react-router-dom";
-import axios from "axios";
-import Home from "./components/Home/Home"; // Has Sidebar +
+import { Routes, Route, Navigate } from "react-router-dom";
+import Home from "./components/Home/Home";
import Register from "./components/Authentication/Register";
import Login from "./components/Authentication/Login";
-import MyFiles from "./components/Home/MyFiles"; // Page
-import HomeContent from "./components/Home/HomeContent"; // Default Content
+import MyFiles from "./components/Home/MyFiles";
+import HomeContent from "./components/Home/HomeContent";
import LandingPage from "./components/Home/LandingPage";
import Analytics from "./components/Home/Analytics";
-// Set up axios defaults
-axios.defaults.baseURL =
- import.meta.env.VITE_API_BASE_URL || "http://localhost:5000";
+import ProtectedRoute from "./components/ProtectedRoute";
-// Add token to requests automatically
-axios.interceptors.request.use((config) => {
- const token = localStorage.getItem("authToken");
- if (token) {
- config.headers.Authorization = `Bearer ${token}`;
- }
- return config;
-});
function App() {
return (
-
+ <>
{/* Public Routes */}
} />
} />
} />
-
- {/* Protected Home Layout */}
- } />
- }>
- } /> {/* /home/myfiles */}
+ } />
+
+ {/* Protected Routes */}
+
+
+
+ }
+ >
+ } />
} />
} />
-
+ >
);
}
-export default App;
+export default App;
\ No newline at end of file
diff --git a/client/src/components/Authentication/Login.tsx b/client/src/components/Authentication/Login.tsx
index f32b4f6..6a1cd1c 100644
--- a/client/src/components/Authentication/Login.tsx
+++ b/client/src/components/Authentication/Login.tsx
@@ -1,6 +1,7 @@
import React, { useState } from "react";
import { useNavigate, Link } from "react-router-dom";
-import axios from "axios";
+// import axios from "axios";
+import api from "../../services/api";
import {
Cloud,
Shield,
@@ -24,29 +25,6 @@ const Login: React.FC = () => {
const [loading, setLoading] = useState(false);
const [errorMsg, setErrorMsg] = useState(null);
- const api = axios.create({
- baseURL: import.meta.env.VITE_API_URL,
- });
-
- const [theme, setTheme] = useState(
- document.documentElement.classList.contains("dark")
- ? "dark" : "light",
- );
-
- const toggleTheme = () => {
- const isDark = document.documentElement.classList.contains("dark");
-
- if (isDark) {
- document.documentElement.classList.remove("dark");
- localStorage.setItem("theme", "light");
- setTheme("light");
- } else {
- document.documentElement.classList.add("dark");
- localStorage.setItem("theme", "dark");
- setTheme("dark");
- }
- };
-
const handleRegisterRedirect = () => {
navigate("/register");
};
@@ -61,27 +39,19 @@ const Login: React.FC = () => {
setLoading(true);
try {
- const response = await api.post("/login", {
- email,
- password,
- });
-
- localStorage.setItem("authToken", response.data.authToken);
- localStorage.setItem("userEmail", email);
+ const response = await api.post(
+ "/login",
+ { email, password },
+ {
+ withCredentials: true,
+ },
+ );
- // Show success message
- setErrorMsg(null);
- alert("Login successful! Welcome back.");
- navigate("/home");
- } catch (error: unknown) {
- if (axios.isAxiosError(error)) {
- setErrorMsg(
- error.response?.data?.error ||
- "Login failed. Please check your credentials.",
- );
- } else {
- setErrorMsg("Login failed. Please try again.");
+ if (response.data.success) {
+ alert("Login successful! Welcome back.");
+ navigate("/home");
}
+ } catch (error: unknown) {
} finally {
setLoading(false);
}
@@ -156,7 +126,7 @@ const Login: React.FC = () => {
-
+
{/* Right Column - Features */}
diff --git a/client/src/components/Authentication/Register.tsx b/client/src/components/Authentication/Register.tsx
index 4497244..ba2e4ef 100644
--- a/client/src/components/Authentication/Register.tsx
+++ b/client/src/components/Authentication/Register.tsx
@@ -1,6 +1,8 @@
+
import React, { useState } from "react";
import { useNavigate, Link } from "react-router-dom";
import axios from "axios";
+import api from "../../services/api";
import {
Shield,
Zap,
@@ -24,10 +26,6 @@ const Register: React.FC = () => {
const [loading, setLoading] = useState(false);
const [errorMsg, setErrorMsg] = useState(null);
- const api = axios.create({
- baseURL: import.meta.env.VITE_API_URL,
- });
-
const handleLogin = () => {
navigate("/login");
};
@@ -54,7 +52,6 @@ const Register: React.FC = () => {
const handleRegister = async () => {
setErrorMsg(null);
- // Basic validation
if (!username || !email || !password) {
setErrorMsg("All fields are required.");
return;
@@ -67,14 +64,15 @@ const Register: React.FC = () => {
setLoading(true);
try {
+
const res = await api.post("/register", {
username,
email,
password,
});
- if (res.data) {
- alert("Registration successful! Please login.");
+ if (res.data.success) {
+ alert("Registration successful! Welcome to SecureShare!");
navigate("/home");
}
} catch (error: unknown) {
@@ -83,7 +81,7 @@ const Register: React.FC = () => {
setErrorMsg(
error.response?.data?.error ||
error.response?.data?.message ||
- "Registration failed. Please check your details.",
+ "Registration failed. Please check your details."
);
} else {
setErrorMsg("Registration failed. Please try again.");
@@ -166,7 +164,7 @@ const Register: React.FC = () => {
-
+
{/* Right Column - Features */}
@@ -472,4 +479,4 @@ const Register: React.FC = () => {
);
};
-export default Register;
+export default Register;
\ No newline at end of file
diff --git a/client/src/components/Home/Home.tsx b/client/src/components/Home/Home.tsx
index 9a85ff8..b8e881c 100644
--- a/client/src/components/Home/Home.tsx
+++ b/client/src/components/Home/Home.tsx
@@ -1,3 +1,4 @@
+
import React, { useState, useEffect } from "react";
import {
LogOut,
@@ -14,6 +15,7 @@ import {
} from "lucide-react";
import { Outlet, useNavigate, useLocation } from "react-router-dom";
import HomeContent from "./HomeContent";
+import api from "../../services/api";
const Home: React.FC = () => {
@@ -22,31 +24,32 @@ const Home: React.FC = () => {
const [isOpen, setIsOpen] = useState(true);
const [isMobile, setIsMobile] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
- const [user] = useState({
- email: localStorage.getItem("userEmail") || "user@example.com",
+ const [user, setUser] = useState({
+ email: "Loading...",
storage: 3.2,
storageLimit: 10,
});
- const [theme, setTheme] = useState(
- document.documentElement.classList.contains("dark")
- ? "dark"
- : "light"
- );
-
- const toggleTheme = () => {
- const isDark = document.documentElement.classList.contains("dark");
-
- if (isDark) {
- document.documentElement.classList.remove("dark");
- localStorage.setItem("theme", "light");
- setTheme("light");
- } else {
- document.documentElement.classList.add("dark");
- localStorage.setItem("theme", "dark");
- setTheme("dark");
- }
- };
+
+ useEffect(() => {
+ const fetchUser = async () => {
+ try {
+ const response = await api.get("/auth", {
+ withCredentials: true,
+ });
+ if (response.data.user) {
+ setUser({
+ email: response.data.user.email,
+ storage: response.data.storageUsed || 3.2,
+ storageLimit: response.data.storageLimit || 10,
+ });
+ }
+ } catch (error) {
+ console.error("Failed to fetch user:", error);
+ }
+ };
+ fetchUser();
+ }, []);
useEffect(() => {
const savedTheme = localStorage.getItem("theme");
@@ -97,10 +100,14 @@ const Home: React.FC = () => {
},
];
- const handleLogout = () => {
- localStorage.removeItem("authToken");
- localStorage.removeItem("userEmail");
- navigate("/login");
+ const handleLogout = async () => {
+ try {
+ await api.post("/logout");
+ navigate("/login");
+ } catch (error) {
+ console.error("Logout failed:", error);
+ navigate("/login");
+ }
};
const handleNavigation = (path: string) => {
@@ -194,8 +201,8 @@ const Home: React.FC = () => {
{isOpen && (
-
- {user.email.split('@')[0]}
+
+ {user.email.split("@")[0]}
{user.email}
@@ -294,8 +301,8 @@ const Home: React.FC = () => {
-
- {user.email.split('@')[0]}
+
+ {user.email.split("@")[0]}
{user.email}
diff --git a/client/src/components/ProtectedRoute.tsx b/client/src/components/ProtectedRoute.tsx
new file mode 100644
index 0000000..6b7f332
--- /dev/null
+++ b/client/src/components/ProtectedRoute.tsx
@@ -0,0 +1,34 @@
+import { Navigate } from "react-router-dom";
+import React from "react";
+
+
+const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
+
+ const [isAuthenticated, setIsAuthenticated] = React.useState
(null);
+
+ React.useEffect(() => {
+ const checkAuth = async () => {
+ try {
+ const response = await fetch("http://localhost:5000/auth", {
+ credentials: "include",
+ });
+ setIsAuthenticated(response.ok);
+ } catch (error) {
+ setIsAuthenticated(false);
+ }
+ };
+ checkAuth();
+ }, []);
+
+ if (isAuthenticated === null) {
+ return Loading...
; // Or a spinner
+ }
+
+ if (!isAuthenticated) {
+ return ;
+ }
+
+ return <>{children}>;
+};
+
+export default ProtectedRoute;
\ No newline at end of file
diff --git a/client/src/main.tsx b/client/src/main.tsx
index 457340a..dd54fc0 100644
--- a/client/src/main.tsx
+++ b/client/src/main.tsx
@@ -1,15 +1,13 @@
-import { StrictMode } from 'react'
-import { createRoot } from 'react-dom/client'
-import './index.css'
-import App from './App.tsx'
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import { BrowserRouter } from "react-router-dom";
+import "./index.css";
+import App from "./App";
-const savedTheme = localStorage.getItem('theme')
-if (savedTheme === 'dark') {
- document.documentElement.classList.add('dark')
-}
-
-createRoot(document.getElementById('root')!).render(
+createRoot(document.getElementById("root")!).render(
-
+
+
+
,
-)
+);
diff --git a/client/src/services/api.ts b/client/src/services/api.ts
index 60af0dd..3b04cdb 100644
--- a/client/src/services/api.ts
+++ b/client/src/services/api.ts
@@ -4,15 +4,7 @@ const API_URL = import.meta.env.VITE_API_URL || "http://localhost:5000";
const api = axios.create({
baseURL: API_URL,
-});
-
-// Add auth token to requests
-api.interceptors.request.use((config) => {
- const token = localStorage.getItem("authToken");
- if (token) {
- config.headers.Authorization = `Bearer ${token}`;
- }
- return config;
+ withCredentials: true,
});
// ==================== ANALYTICS APIs ====================
@@ -68,4 +60,6 @@ export const fileApi = {
const response = await api.delete(`/api/files/${fileId}`);
return response.data;
},
-};
\ No newline at end of file
+};
+
+export default api;
\ No newline at end of file
diff --git a/server/config/redis.js b/server/config/redis.js
new file mode 100644
index 0000000..ecfc9ad
--- /dev/null
+++ b/server/config/redis.js
@@ -0,0 +1,12 @@
+import { createClient } from "redis";
+
+const redisClient = createClient({
+ url: process.env.REDIS_URL || "redis://localhost:6379"
+});
+
+redisClient.on("error", (err) => console.log("Redis Error:", err));
+redisClient.on("connect", () => console.log("Redis Connected Successfully!"));
+
+await redisClient.connect();
+
+export default redisClient;
\ No newline at end of file
diff --git a/server/controllers/userController.js b/server/controllers/userController.js
index 0b20a9a..0e884e5 100644
--- a/server/controllers/userController.js
+++ b/server/controllers/userController.js
@@ -1,6 +1,7 @@
import User from "../models/UserSchema.js";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
+import redisClient from "../config/redis.js";
// REGISTER
export const registerUser = async (req, res) => {
@@ -88,48 +89,41 @@ export const registerUser = async (req, res) => {
};
// LOGIN
+
export const loginUser = async (req, res) => {
const { email, password } = req.body;
try {
- // Validate input
if (!email || !password) {
return res.status(400).json({ error: "Email and password are required" });
}
- // Find user by email (case insensitive)
- const user = await User.findOne({
- email: email.toLowerCase().trim(),
- });
-
+ const user = await User.findOne({ email: email.toLowerCase().trim() });
if (!user) {
- return res.status(401).json({ error: "Invalid credentials" }); // 401 for unauthorized
+ return res.status(401).json({ error: "Invalid credentials" });
}
- // Compare password
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(401).json({ error: "Invalid credentials" });
}
// Generate JWT token
- const payload = {
- user: {
- id: user.id,
- email: user.email,
- },
- };
+ const payload = { user: { id: user.id, email: user.email } };
+ const token = jwt.sign(payload, process.env.JWT_SECRET, {
+ expiresIn: process.env.JWT_EXPIRES_IN || "7d"
+ });
- const authToken = jwt.sign(
- payload,
- process.env.JWT_SECRET || process.env.JWT_TOKEN,
- {
- expiresIn: process.env.JWT_EXPIRES_IN || "1h",
- }
- );
+ res.cookie("token", token, {
+ httpOnly: true,
+ secure: false,
+ sameSite: "lax",
+ path: "/",
+ maxAge: 7 * 24 * 60 * 60 * 1000
+ });
res.json({
- authToken,
+ success: true,
message: "Login successful",
user: {
id: user.id,
@@ -139,11 +133,39 @@ export const loginUser = async (req, res) => {
});
} catch (error) {
console.error("Login error:", error.message);
- res.status(500).json({
- error: "Server Error",
- message:
- process.env.NODE_ENV === "development" ? error.message : undefined,
+ res.status(500).json({ error: "Server Error" });
+ }
+};
+
+
+// LOGOUT
+
+export const logoutUser = async (req, res) => {
+ try {
+ const token = req.cookies?.token;
+
+ if (token) {
+ const decoded = jwt.decode(token);
+ if (decoded && decoded.exp) {
+ const ttl = decoded.exp - Math.floor(Date.now() / 1000);
+ if (ttl > 0) {
+ await redisClient.setEx(`blacklist:${token}`, ttl, "blocked");
+ console.log("✅ Token blacklisted");
+ }
+ }
+ }
+
+ res.clearCookie("token", {
+ httpOnly: true,
+ secure: false,
+ sameSite: "lax",
+ path: "/",
});
+
+ res.status(200).json({ success: true, message: "Logged out successfully" });
+ } catch (error) {
+ console.error("Logout error:", error);
+ res.status(500).json({ error: "Logout failed" });
}
};
diff --git a/server/index.js b/server/index.js
index 297ea1a..3d54340 100644
--- a/server/index.js
+++ b/server/index.js
@@ -1,9 +1,11 @@
+
import express from "express";
import dotenv from "dotenv";
dotenv.config();
import connectDB from "./config/db.js";
import router from "./routes/routers.js";
import cors from "cors";
+import cookieParser from "cookie-parser";
import multer from "multer";
import { v2 as cloudinary } from "cloudinary";
import streamifier from "streamifier";
@@ -12,10 +14,10 @@ import fileRoutes from "./routes/files.js";
const app = express();
-// CORS setup
+
app.use(
cors({
- origin: true,
+ origin: "http://localhost:5173",
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
credentials: true,
}),
@@ -24,18 +26,14 @@ app.use(
// Connect to database
connectDB();
-// Seed demo user
-seedDemoUser();
-// Middleware
app.use(express.json());
-// In your server.js or app.js
+app.use(cookieParser());
+// Routes
app.use("/api/analytics", analyticsRoutes);
app.use("/api/track", analyticsRoutes);
app.use("/api/files", fileRoutes);
-
-// Routes
app.use("/", router);
// Error handling middleware
@@ -78,7 +76,7 @@ app.post("/upload", upload.single("file"), async (req, res) => {
};
const result = await streamUpload(req.file);
- res.json({ url: result.secure_url }); // Public Cloudinary URL
+ res.json({ url: result.secure_url });
} catch (err) {
console.error(err);
res.status(500).json({ error: "Upload failed" });
@@ -89,4 +87,4 @@ app.post("/upload", upload.single("file"), async (req, res) => {
const PORT = process.env.PORT || 5000;
app.listen(PORT, () =>
console.log(`Server running on port http://localhost:${PORT}`),
-);
+);
\ No newline at end of file
diff --git a/server/middleware/authenticationUser.js b/server/middleware/authenticationUser.js
index 551da81..501e61a 100644
--- a/server/middleware/authenticationUser.js
+++ b/server/middleware/authenticationUser.js
@@ -1,28 +1,49 @@
import jwt from "jsonwebtoken";
+import redisClient from "../config/redis.js";
-const authenticateUser = (req, res, next) => {
- const authHeader = req.header("Authorization");
+const authenticateUser = async (req, res, next) => {
+ let token = req.cookies?.token;
- // Check if Authorization header exists
- if (!authHeader) {
- return res.status(401).json({ error: "No token provided, authorization denied" });
+ if (!token) {
+ const authHeader = req.header("Authorization");
+ if (authHeader && authHeader.startsWith("Bearer ")) {
+ token = authHeader.split(" ")[1];
+ }
}
- // Extract token from "Bearer " format
- const token = authHeader.split(" ")[1];
if (!token) {
- return res.status(401).json({ error: "Invalid token format, authorization denied" });
+ return res
+ .status(401)
+ .json({ error: "No token provided, authorization denied" });
+ }
+
+ try {
+ const blacklisted = await redisClient.get(`blacklist:${token}`);
+ if (blacklisted) {
+ return res
+ .status(401)
+ .json({ error: "Token has been invalidated. Please login again." });
+ }
+ } catch (redisError) {
+ console.error("Redis blacklist check error:", redisError);
}
try {
- // Verify token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded.user; // { id: user.id }
+ req.token = token; // Store token for potential logout use
next();
} catch (err) {
console.error("Token verification failed:", err.message);
+
+ if (err.name === "TokenExpiredError") {
+ return res
+ .status(401)
+ .json({ error: "Token expired, please login again" });
+ }
+
return res.status(401).json({ error: "Token is not valid" });
}
};
-export default authenticateUser;
\ No newline at end of file
+export default authenticateUser;
diff --git a/server/node_modules/.package-lock.json b/server/node_modules/.package-lock.json
index b6ce44a..c1acb3c 100644
--- a/server/node_modules/.package-lock.json
+++ b/server/node_modules/.package-lock.json
@@ -13,6 +13,78 @@
"sparse-bitfield": "^3.0.3"
}
},
+ "node_modules/@redis/bloom": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmmirror.com/@redis/bloom/-/bloom-5.12.1.tgz",
+ "integrity": "sha512-PUUfv+ms7jgPSBVoo/DN4AkPHj4D5TZSd6SbJX7egzBplkYUcKmHRE8RKia7UtZ8bSQbLguLvxVO+asKtQfZWA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.12.1"
+ }
+ },
+ "node_modules/@redis/client": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmmirror.com/@redis/client/-/client-5.12.1.tgz",
+ "integrity": "sha512-7aPGWeqA3uFm43o19umzdl16CEjK/JQGtSXVPevplTaOU3VJA/rseBC1QvYUz9lLDIMBimc4SW/zrW4S89BaCA==",
+ "license": "MIT",
+ "dependencies": {
+ "cluster-key-slot": "1.1.2"
+ },
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@node-rs/xxhash": "^1.1.0",
+ "@opentelemetry/api": ">=1 <2"
+ },
+ "peerDependenciesMeta": {
+ "@node-rs/xxhash": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@redis/json": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmmirror.com/@redis/json/-/json-5.12.1.tgz",
+ "integrity": "sha512-eOze75esLve4vfqDel7aMX08CNaiLLQS2fV8mpRN9NxPe1rVR4vQyYiW/OgtGUysF6QOr9ANhfxABKNOJfXdKg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.12.1"
+ }
+ },
+ "node_modules/@redis/search": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmmirror.com/@redis/search/-/search-5.12.1.tgz",
+ "integrity": "sha512-ItlxbxC9cKI6IU1TLWoczwJCRb6TdmkEpWv05UrPawqaAnWGRu3rcIqsc5vN483T2fSociuyV1UkWIL5I4//2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.12.1"
+ }
+ },
+ "node_modules/@redis/time-series": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmmirror.com/@redis/time-series/-/time-series-5.12.1.tgz",
+ "integrity": "sha512-c6JL6E3EcZJuNqKFz+KM+l9l5mpcQiKvTwgA3blt5glWJ8hjDk0yeHN3beE/MpqYIQ8UEX44ItQzgkE/gCBELQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.12.1"
+ }
+ },
"node_modules/@types/webidl-conversions": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
@@ -242,6 +314,15 @@
"node": ">=9"
}
},
+ "node_modules/cluster-key-slot": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
+ "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -294,6 +375,25 @@
"node": ">= 0.6"
}
},
+ "node_modules/cookie-parser": {
+ "version": "1.4.7",
+ "resolved": "https://registry.npmmirror.com/cookie-parser/-/cookie-parser-1.4.7.tgz",
+ "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "0.7.2",
+ "cookie-signature": "1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/cookie-parser/node_modules/cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
+ "license": "MIT"
+ },
"node_modules/cookie-signature": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
@@ -1359,6 +1459,22 @@
"node": ">=8.10.0"
}
},
+ "node_modules/redis": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmmirror.com/redis/-/redis-5.12.1.tgz",
+ "integrity": "sha512-LDsoVvb/CpoV9EN3FXvgvSHNJWuCIzl9MiO3ppOevuGLpSGJhwfQjpEwfFJcQvNSddHADDdZaWx0HnmMxRXG7g==",
+ "license": "MIT",
+ "dependencies": {
+ "@redis/bloom": "5.12.1",
+ "@redis/client": "5.12.1",
+ "@redis/json": "5.12.1",
+ "@redis/search": "5.12.1",
+ "@redis/time-series": "5.12.1"
+ },
+ "engines": {
+ "node": ">= 18.19.0"
+ }
+ },
"node_modules/router": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
diff --git a/server/package-lock.json b/server/package-lock.json
index 3a59da9..80da2f2 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -11,6 +11,7 @@
"dependencies": {
"bcryptjs": "^3.0.2",
"cloudinary": "^2.7.0",
+ "cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dotenv": "^16.5.0",
"express": "^5.1.0",
@@ -19,6 +20,7 @@
"mongoose": "^8.15.1",
"multer": "^2.0.2",
"path": "^0.12.7",
+ "redis": "^5.12.1",
"streamifier": "^0.1.1"
},
"devDependencies": {
@@ -37,6 +39,78 @@
"sparse-bitfield": "^3.0.3"
}
},
+ "node_modules/@redis/bloom": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmmirror.com/@redis/bloom/-/bloom-5.12.1.tgz",
+ "integrity": "sha512-PUUfv+ms7jgPSBVoo/DN4AkPHj4D5TZSd6SbJX7egzBplkYUcKmHRE8RKia7UtZ8bSQbLguLvxVO+asKtQfZWA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.12.1"
+ }
+ },
+ "node_modules/@redis/client": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmmirror.com/@redis/client/-/client-5.12.1.tgz",
+ "integrity": "sha512-7aPGWeqA3uFm43o19umzdl16CEjK/JQGtSXVPevplTaOU3VJA/rseBC1QvYUz9lLDIMBimc4SW/zrW4S89BaCA==",
+ "license": "MIT",
+ "dependencies": {
+ "cluster-key-slot": "1.1.2"
+ },
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@node-rs/xxhash": "^1.1.0",
+ "@opentelemetry/api": ">=1 <2"
+ },
+ "peerDependenciesMeta": {
+ "@node-rs/xxhash": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@redis/json": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmmirror.com/@redis/json/-/json-5.12.1.tgz",
+ "integrity": "sha512-eOze75esLve4vfqDel7aMX08CNaiLLQS2fV8mpRN9NxPe1rVR4vQyYiW/OgtGUysF6QOr9ANhfxABKNOJfXdKg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.12.1"
+ }
+ },
+ "node_modules/@redis/search": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmmirror.com/@redis/search/-/search-5.12.1.tgz",
+ "integrity": "sha512-ItlxbxC9cKI6IU1TLWoczwJCRb6TdmkEpWv05UrPawqaAnWGRu3rcIqsc5vN483T2fSociuyV1UkWIL5I4//2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.12.1"
+ }
+ },
+ "node_modules/@redis/time-series": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmmirror.com/@redis/time-series/-/time-series-5.12.1.tgz",
+ "integrity": "sha512-c6JL6E3EcZJuNqKFz+KM+l9l5mpcQiKvTwgA3blt5glWJ8hjDk0yeHN3beE/MpqYIQ8UEX44ItQzgkE/gCBELQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18.19.0"
+ },
+ "peerDependencies": {
+ "@redis/client": "^5.12.1"
+ }
+ },
"node_modules/@types/webidl-conversions": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
@@ -266,6 +340,15 @@
"node": ">=9"
}
},
+ "node_modules/cluster-key-slot": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
+ "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -318,6 +401,25 @@
"node": ">= 0.6"
}
},
+ "node_modules/cookie-parser": {
+ "version": "1.4.7",
+ "resolved": "https://registry.npmmirror.com/cookie-parser/-/cookie-parser-1.4.7.tgz",
+ "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "0.7.2",
+ "cookie-signature": "1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/cookie-parser/node_modules/cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
+ "license": "MIT"
+ },
"node_modules/cookie-signature": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
@@ -1398,6 +1500,22 @@
"node": ">=8.10.0"
}
},
+ "node_modules/redis": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmmirror.com/redis/-/redis-5.12.1.tgz",
+ "integrity": "sha512-LDsoVvb/CpoV9EN3FXvgvSHNJWuCIzl9MiO3ppOevuGLpSGJhwfQjpEwfFJcQvNSddHADDdZaWx0HnmMxRXG7g==",
+ "license": "MIT",
+ "dependencies": {
+ "@redis/bloom": "5.12.1",
+ "@redis/client": "5.12.1",
+ "@redis/json": "5.12.1",
+ "@redis/search": "5.12.1",
+ "@redis/time-series": "5.12.1"
+ },
+ "engines": {
+ "node": ">= 18.19.0"
+ }
+ },
"node_modules/router": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
diff --git a/server/package.json b/server/package.json
index be93803..96c14da 100644
--- a/server/package.json
+++ b/server/package.json
@@ -14,6 +14,7 @@
"dependencies": {
"bcryptjs": "^3.0.2",
"cloudinary": "^2.7.0",
+ "cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dotenv": "^16.5.0",
"express": "^5.1.0",
@@ -22,6 +23,7 @@
"mongoose": "^8.15.1",
"multer": "^2.0.2",
"path": "^0.12.7",
+ "redis": "^5.12.1",
"streamifier": "^0.1.1"
},
"devDependencies": {
diff --git a/server/routes/routers.js b/server/routes/routers.js
index 8c9ff39..8453487 100644
--- a/server/routes/routers.js
+++ b/server/routes/routers.js
@@ -7,7 +7,7 @@ import {
import { validation } from "../middleware/validation.js";
import { loginValidation } from "../middleware/loginValidation.js";
import authenticateUser from "../middleware/authenticationUser.js";
-
+import { logoutUser } from "../controllers/userController.js";
const router = express.Router();
@@ -17,6 +17,10 @@ router.post("/register", validation, registerUser);
// Login
router.post("/login", loginValidation, loginUser);
+//logout
+
+router.post("/logout", authenticateUser, logoutUser);
+
// Get current user
router.get("/auth", authenticateUser, getUser);