const express = require("express"); const mongoose = require("mongoose"); const cors = require("cors"); const session = require("express-session"); const passport = require("passport"); const bodyParser = require("body-parser"); const path = require("path"); const bcrypt = require("bcryptjs"); const jwt = require("jsonwebtoken"); const cookieparser = require("cookie-parser"); require("dotenv").config(); // Import Routes const authRoutes = require("./routes/authRoutes"); const courseRoutes = require("./routes/courseRoutes"); const facultyRoutes = require("./routes/facultyRoutes"); const appointmentRoutes = require("./routes/appointmentRoutes"); const optionsRoutes = require("./routes/optionsRoutes"); const consolidatedRoutes = require("./routes/consolidatedRoutes"); const emailRoutes = require("./routes/emailRoutes"); const Course = require("./models/Course"); // MongoDB Connection mongoose .connect(process.env.mongoURI, { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log("MongoDB connected")) .catch((err) => { console.error("MongoDB connection error:", err); process.exit(1); // Exit the app if the database connection fails }); // Initialize App const app = express(); const PORT = 8080; // Middleware app.use(cors({ origin: "http://localhost:3000", credentials: true })); app.use(cookieparser()); app.use(express.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use( session({ secret: "secret", // This can be replaced with another secret from .env if required resave: false, saveUninitialized: false, }) ); app.use(passport.initialize()); app.use(passport.session()); app.use("/api/table", consolidatedRoutes); // Passport Config require("./config/passport"); // Routes app.use("/password", authRoutes); app.use("/api/courses", courseRoutes); app.use("/api/faculty", facultyRoutes); app.use("/api/appointments", appointmentRoutes); app.use("/api/options", optionsRoutes); app.use("/api/data", consolidatedRoutes); app.use("/api/send-email", emailRoutes); // Google OAuth Routes app.get( "/auth/google", passport.authenticate("google", { scope: ["profile", "email"] }) ); app.get( "/auth/google/callback", passport.authenticate("google", { failureRedirect: "/" }), (req, res) => { const token = jwt.sign({ userId: req.user._id }, process.env.JWT_SECRET, { expiresIn: "1h" }); // Set token as a cookie or send it in the response res.cookie("token", token, { httpOnly: true, secure: false, maxAge: 3600000 }); res.redirect("http://localhost:3000/Welcome"); // Redirect to a frontend route after successful login } ); // Local authentication routes (register and login) app.post("/api/register", async (req, res) => { try { const { username, email, password } = req.body; const hashedPassword = await bcrypt.hash(password, 10); let user = await User.findOne({ email }); if (user) { if (user.googleId && user.password) { return res.status(400).json({ message: "User already exists" }); } if (!user.googleId && user.password) { return res.status(400).json({ message: "User already exists" }); } if (user.googleId && !user.password) { user.password = hashedPassword; await user.save(); } } else { user = new User({ username, email, password: hashedPassword }); await user.save(); } // Generate a JWT token using the user's ID const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: "1h" }); // Set the token as a cookie res.cookie("token", token, { httpOnly: true, secure: false, maxAge: 3600000 }); // 1 hour expiry return res.status(200).json({ message: "Registered and logged in successfully", user, }); } catch (error) { console.error("Error registering user:", error); res.status(400).send("Registration failed"); } }); app.post("/api/login", (req, res, next) => { passport.authenticate("local", (err, user, info) => { if (err) { return res.status(500).json({ message: "Internal server error" }); } if (!user) { return res.status(401).json({ message: "Incorrect email or password" }); } req.logIn(user, (err) => { if (err) { return res.status(500).json({ message: "Internal server error" }); } // Generate a JWT token using the user's ID const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: "1h" }); // Set the token as a cookie res.cookie("token", token, { httpOnly: true, secure: false, maxAge: 3600000 }); // 1 hour expiry return res.status(200).json({ message: "Login successful", user }); }); })(req, res, next); }); app.get("/auth/logout", function (req, res) { try { // Clear the token cookie res.clearCookie("token", { httpOnly: true, // Ensure it matches the cookie options you set earlier secure: false, // Match the "secure" option from cookie settings sameSite: "lax", // Ensure this matches the original cookie configuration }); // Destroy the session if used (optional, if sessions are implemented) if (req.session) { req.session.destroy((err) => { if (err) { console.error("Error destroying session:", err); return res.status(500).json({ message: "Error logging out" }); } res.status(200).json({ message: "Logout successful, session destroyed" }); }); } else { // If no session, simply respond with success res.status(200).json({ message: "Logout successful, cookie cleared" }); } } catch (err) { console.error("Error logging out:", err); res.status(500).json({ message: "Error logging out" }); } }); // Refresh Token Endpoint app.post("/api/refresh", (req, res) => { const refreshToken = req.cookies.token; console.log(refreshToken); if (!refreshToken) { return res.status(401).json({ message: "No refresh token, authorization denied" }); } try { const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET); const newToken = jwt.sign({ userId: decoded.userId }, process.env.JWT_SECRET, { expiresIn: "1h" }); return res .cookie("token", newToken, { httpOnly: true, maxAge: 3600000 }) // Set new access token .status(200) .json({ message: "Token refreshed" }); } catch (err) { console.error("Error refreshing token:", err); res.status(401).json({ message: "Invalid or expired refresh token" }); } }); app.get("/api/auth-check", (req, res) => { const token = req.cookies.token; // Retrieve the httpOnly cookie if (!token) { return res.status(401).json({ message: "Unauthorized" }); } try { jwt.verify(token, process.env.JWT_SECRET); // Verify the token res.status(200).json({ authenticated: true }); // Valid token } catch (err) { res.status(401).json({ message: "Unauthorized" }); // Invalid token } }); // User Profile Route app.get("/api/user/profile", async (req, res) => { try { if (req.user) { return res.json({ user: req.user }); } else { return res.status(401).json({ error: "Unauthorized" }); } } catch (error) { console.error("Error fetching user data:", error); res.status(500).json({ message: "Internal server error" }); } }); app.patch("/api/courses/:courseId", async (req, res) => { const { courseId } = req.params; const { status } = req.body; console.log("Request params:", req.params); console.log("Request body:", req.body); if (!status) { console.error("Status is missing in the request body."); return res.status(400).json({ message: "Status is required" }); } try { const updatedCourse = await Course.findOneAndUpdate( { courseId: courseId }, // Use courseId field for finding the course { status }, // Update the status field { new: true } // Return the updated document ); if (!updatedCourse) { console.error("Course not found:", courseId); return res.status(404).json({ message: "Course not found" }); } res.json(updatedCourse); } catch (error) { console.error("Error updating course status:", error.message); res.status(500).json({ message: "Internal server error" }); } }); // Serve React Build Files app.use(express.static(path.join(__dirname, "../client/build"))); app.get("*", (req, res) => res.sendFile(path.join(__dirname, "../client/build/index.html")) ); // Error Handling Middleware app.use((err, req, res, next) => { console.error("Error:", err.stack); res.status(err.status || 500).json({ error: err.message || "Internal Server Error" }); }); // Start Server app.listen(PORT, () => { console.log(`Server is running at http://localhost:8080`); });