Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# ========================
# Server Configuration
# ========================
PORT=4000

# ========================
# MongoDB Connection
# ========================
# Replace with your MongoDB connection string
MONGO_URI=your_mongodb_connection_string_here
# ========================
# JWT Secret
# ========================
# This secret is used to sign JWT tokens
JWT_SECRET=myverysecurejwtsecret123!

2 changes: 2 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env/
node_modules/
50 changes: 28 additions & 22 deletions backend/controllers/authController.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,23 @@ const generateToken = (id) => {
export const registerUser = async (req, res) => {
const { name, email, password } = req.body;

// Check if user already exists

const userExists = await User.findOne({ email });
if (userExists) {
return res.status(400).json({ message: 'User already exists' });
}
if (userExists) return res.status(400).json({ message: 'User already exists' });

// Hash password
const hashedPassword = await bcrypt.hash(password, 10);

// Create user

const user = await User.create({ name, email, password: hashedPassword });

// Generate JWT


const token = generateToken(user._id);

// Set JWT in secure, HTTP-only cookie
res.cookie('token', token, {
httpOnly: true, // Prevent JS access (XSS protection)
sameSite: 'strict', // CSRF protection
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
httpOnly: true,
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000,
});

res.status(201).json({
Expand All @@ -41,22 +38,14 @@ export const registerUser = async (req, res) => {
export const loginUser = async (req, res) => {
const { email, password } = req.body;

// Find user
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ message: 'User not found' });
}
if (!user) return res.status(401).json({ message: 'User not found' });

// Compare password
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(401).json({ message: 'Invalid password' });
}
if (!isMatch) return res.status(401).json({ message: 'Invalid password' });

// Generate JWT
const token = generateToken(user._id);

// Set JWT in secure, HTTP-only cookie
res.cookie('token', token, {
httpOnly: true,
sameSite: 'strict',
Expand All @@ -71,7 +60,6 @@ export const loginUser = async (req, res) => {
};

export const logoutUser = async (req, res) => {
// Clear the JWT cookie
res.cookie('token', '', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
Expand All @@ -81,3 +69,21 @@ export const logoutUser = async (req, res) => {

res.status(200).json({ message: 'User logged out successfully' });
};

// βœ… New: Get current user from HTTP-only cookie
export const getCurrentUser = async (req, res) => {
try {
const token = req.cookies?.token;
if (!token) return res.status(401).json({ success: false, message: 'Not authenticated' });

const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findById(decoded.id).select('-password');

if (!user) return res.status(404).json({ success: false, message: 'User not found' });

res.json({ success: true, user });
} catch (err) {
console.error('getCurrentUser error:', err);
res.status(401).json({ success: false, message: 'Invalid or expired token' });
}
};
8 changes: 4 additions & 4 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"react-to-print": "^3.1.1",
"react-toastify": "^11.0.5",
"react-tooltip": "^4.5.1",
"vite": "^7.1.1"
"vite": "^7.1.10"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
Expand Down
163 changes: 76 additions & 87 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect } from "react";
import Navbar from "./components/Navbar/Navbar";
import { Routes, Route, useLocation } from "react-router-dom";
import Home from "./pages/Home/Home";
Expand All @@ -20,51 +20,92 @@ import ContactPage from "./pages/Contactpage";
import { Toaster } from "react-hot-toast";
import LoadingAnimation from "./components/LoadingAnimation";
import ScrollToTop from "../utility/ScrollToTop";
import "./components/FoodDetail/print.css";
import NotFound from "./pages/Notfound";
import StoreContextProvider from "./components/context/StoreContext";
import ScrollToBottom from "./components/ScrollToBottomButton/ScrollToBottomButton";
import ReferralProgram from "./components/Referrals/ReferralProgram";
import AboutUs from "./components/Aboutus/Aboutus";
import FAQ from "./components/FAQ/FAQ";
import MyProfile from "./pages/MyProfile/MyProfile";
import MyOrder from "./pages/MyOrder/MyOrder";
import Privacy from "./components/Privacy/privacy";
import StoreContextProvider from "./components/context/StoreContext";
import apiRequest from "./lib/apiRequest";
import SuccessPopup from "./components/LoginPopup/SuccessPopup";
import "./components/FoodDetail/print.css";
import NotFound from "./pages/Notfound";
import TermsOfService from "./components/TermsOfService/TermsOfService";
import Delivery from "./pages/Delivery/Delivery";

const App = () => {
const [showLogin, setShowLogin] = useState(false);
const [loading, setLoading] = useState(true);
const [isLoggedIn, setIsLoggedIn] = useState(() => {
return !!localStorage.getItem("authToken") || !!localStorage.getItem("user");
});
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [user, setUser] = useState(null);
const [successMessage, setSuccessMessage] = useState("");

// Loading animation
useEffect(() => {
const timer = setTimeout(() => setLoading(false), 3000);
const timer = setTimeout(() => setLoading(false), 2000);
return () => clearTimeout(timer);
}, []);

// Check authentication from backend
useEffect(() => {
const checkAuth = async () => {
try {
const response = await apiRequest.get("/api/auth/me", {
withCredentials: true,
});
if (response.data.success) {
setIsLoggedIn(true);
setUser(response.data.user);
} else {
setIsLoggedIn(false);
setUser(null);
}
} catch (err) {
console.log("Auth check failed:", err);
setIsLoggedIn(false);
setUser(null);
}
};

checkAuth();

// Listen to localStorage changes (optional, if frontend still uses it)
const handleStorageChange = () => {
setIsLoggedIn(!!localStorage.getItem("authToken"));
const token = localStorage.getItem("authToken");
setIsLoggedIn(!!token);
// Optionally set user info from localStorage
};
window.addEventListener("storage", handleStorageChange);
return () => window.removeEventListener("storage", handleStorageChange);
}, []);

if (loading) {
return <LoadingAnimation />;
}
if (loading) return <LoadingAnimation />;

return (
<ThemeContextProvider>
<StoreContextProvider>
<Toaster position="top-right" reverseOrder={false} />
{showLogin && <LoginPopup setShowLogin={setShowLogin} setIsLoggedIn={setIsLoggedIn} />}

{successMessage && (
<SuccessPopup
message={successMessage}
onClose={() => setSuccessMessage("")}
/>
)}

{showLogin && (
<LoginPopup
setShowLogin={setShowLogin}
setIsLoggedIn={setIsLoggedIn}
setUser={setUser}
setSuccessMessage={setSuccessMessage}
/>
)}

<div className="app">
<Navbar setShowLogin={setShowLogin} setIsLoggedIn={setIsLoggedIn} />
<Navbar setShowLogin={setShowLogin} setIsLoggedIn={setIsLoggedIn} user={user} />
<ScrollToTop />
<ScrollToBottom />

Expand All @@ -74,26 +115,7 @@ const App = () => {
<Route
path="/order"
element={
isLoggedIn ? (
<PlaceOrder />
) : (
<div style={{ padding: "2rem", textAlign: "center" }}>
<h2
style={{
color: "#f97316",
fontSize: "2rem",
fontWeight: "bold",
textShadow: "1px 1px 2px rgba(0,0,0,0.2)",
marginBottom: "0.5rem",
}}
>
Please Log In To Proceed
</h2>
<p style={{ color: "#fdba74", fontSize: "1rem" }}>
Your journey continues after login πŸ”
</p>
</div>
)
isLoggedIn ? <PlaceOrder /> : <LoginRequired />
}
/>
<Route path="/faq" element={<FAQ />} />
Expand All @@ -105,56 +127,8 @@ const App = () => {
<Route path="/referral" element={<ReferralProgram />} />
<Route path="/restaurant/:id" element={<RestaurantDetail />} />
<Route path="/aboutus" element={<AboutUs />} />
<Route
path="/profile/me"
element={
isLoggedIn ? (
<MyProfile />
) : (
<div style={{ padding: "2rem", textAlign: "center" }}>
<h2
style={{
color: "#f97316",
fontSize: "2rem",
fontWeight: "bold",
textShadow: "1px 1px 2px rgba(0,0,0,0.2)",
marginBottom: "0.5rem",
}}
>
Please Log In To Proceed
</h2>
<p style={{ color: "#fdba74", fontSize: "1rem" }}>
Your journey continues after login πŸ”
</p>
</div>
)
}
/>
<Route
path="/orders/me"
element={
isLoggedIn ? (
<MyOrder />
) : (
<div style={{ padding: "2rem", textAlign: "center" }}>
<h2
style={{
color: "#f97316",
fontSize: "2rem",
fontWeight: "bold",
textShadow: "1px 1px 2px rgba(0,0,0,0.2)",
marginBottom: "0.5rem",
}}
>
Please Log In To Proceed
</h2>
<p style={{ color: "#fdba74", fontSize: "1rem" }}>
Your journey continues after login πŸ”
</p>
</div>
)
}
/>
<Route path="/profile/me" element={isLoggedIn ? <MyProfile /> : <LoginRequired />} />
<Route path="/orders/me" element={isLoggedIn ? <MyOrder /> : <LoginRequired />} />
<Route path="/privacy" element={<Privacy />} />
<Route path="/terms" element={<TermsOfService />} />
<Route path="/delivery" element={<Delivery />} />
Expand All @@ -164,9 +138,6 @@ const App = () => {
<ScrollToTopButton />
<CartSummaryBar />
<AppDownload />

{/* Removed FeedbackReviews from here */}

<Footer />
<Chatbot />
</div>
Expand All @@ -175,4 +146,22 @@ const App = () => {
);
};

// Reusable login-required component
const LoginRequired = () => (
<div style={{ padding: "2rem", textAlign: "center" }}>
<h2
style={{
color: "#f97316",
fontSize: "2rem",
fontWeight: "bold",
textShadow: "1px 1px 2px rgba(0,0,0,0.2)",
marginBottom: "0.5rem",
}}
>
Please Log In To Proceed
</h2>
<p style={{ color: "#fdba74", fontSize: "1rem" }}>Your journey continues after login πŸ”</p>
</div>
);

export default App;
Loading