require("dotenv").config(); const express = require("express"); const session = require("express-session"); const port = process.env.APP_PORT; const app = express(); const cookieParser = require("cookie-parser"); const path = require("path"); const { insertUser, isUserExists, changePassword, db, } = require("./backend/db.js"); const { initializeSocket } = require("./backend/socket.js"); const bcrypt = require("bcrypt"); const saltRounds = 10; const { createServer } = require("node:http"); const server = createServer(app); const jwt = require("jsonwebtoken"); const { decode } = require("jsonwebtoken"); const { json } = require("express"); const jwtSecret = process.env.JWT_SECRET; app.use(cookieParser()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(express.static(path.join(__dirname, "public"))); app.use("/static", express.static("frontend")); app.use( session({ secret: process.env.SESSION_SECRET, resave: true, saveUninitialized: true, cookie: { secure: false, maxAge: 30 * 24 * 60 * 60 * 1000, //30 days }, }), ); // auth login API app.post("/auth/login", async (req, res) => { await loginUser(req, res); }); // auth signup API app.post("/auth/signup", async (req, res) => { await signupUser(req, res); }); // logout API app.post("/auth/logout", (req, res) => { // clear JWT token res.clearCookie("token", { path: "/", }); // clear socket.io cookie (no idea what is it for but better remove it) res.clearCookie("io", { path: "/", }); res.status(200).json({ message: "Successfully logged out" }); }); // get JWT token API app.get("/auth/token", (req, res) => { const token = req.cookies.token; if (!token) { res.send("Not logged in"); } res.send(token); }); app.post("/auth/changePassword", async (req, res) => { const token = req.cookies.token; const { cPassword, nPassword } = req.body; if (!cPassword && nPassword) { return res.json({ message: "Field is empty" }); } if (nPassword === cPassword) { return res.json({ message: "Passwords are the same" }); } let username; try { const decoded = jwt.verify(token, jwtSecret); username = decoded.username; } catch (err) { return res.status(403).json({ message: "Unauthorized" }); } try { const result = await db.query( "SELECT * FROM accounts WHERE username = $1", [username], ); // checks that passwords are matching const match = await bcrypt.compare(cPassword, result.rows[0].password); // if not return information if (!match) { return res.status(401).json({ message: "Current password is invalid" }); } // hash password const salt = await bcrypt.genSalt(saltRounds); const hash = await bcrypt.hash(nPassword, salt); await changePassword(username, hash); return res.status(200).json({ message: "Successfully changed password" }); } catch (err) { return res.status(500).json({ message: "Failed to change password" }); } }); // get username app.get("/auth/user", (req, res) => { const token = req.cookies.token; // verify token if (token) { jwt.verify(token, jwtSecret, (err, user) => { if (err) { return res.status(403).send("Unauthorized"); } else { const username = user.username; res.json({ username }); } }); } }); app.post("/api/contacts", async (req, res) => { const token = req.cookies.token; if (token) { jwt.verify(token, jwtSecret, (err, user) => { if (err) { return res.status(403).send("Unauthorized"); } }); } const jsonContacts = JSON.stringify(req.body.contactUsername); }); // serving the login page app.get("/login", (req, res) => { const token = req.cookies.token; // verify token if (token) { res.json({ Error: "Already logged in" }); } else { res.sendFile(path.join(__dirname, "/frontend/routes/login.html")); } }); // serving the signup page app.get("/signup", (req, res) => { const token = req.cookies.token; if (token) { res.json({ Error: "Already logged in" }); } else res.sendFile(path.join(__dirname, "/frontend/routes/signup.html")); }); app.get("/settings", (req, res) => { const token = req.cookies.token; if (!token) { res.redirect("/login"); return; } // verify token jwt.verify(token, jwtSecret, (err) => { if (err) { return res.status(403).send("Unauthorized"); } res.sendFile(path.join(__dirname, "/frontend/routes/settings.html")); }); }); app.get("/", (req, res) => { const token = req.cookies.token; if (!token) { return res.redirect("/login"); } else { return res.redirect("/chat"); } }); // serving the chat page if logged in app.get("/chat", (req, res) => { const token = req.cookies.token; if (!token) { res.redirect("/login"); return; } // verify token jwt.verify(token, jwtSecret, (err) => { if (err) { return res.status(403).send("Unauthorized"); } res.sendFile(path.join(__dirname, "/frontend/routes/chat.html")); }); }); initializeSocket(server); // run server server.listen(port, () => { console.log(`Chat app listening on port ${port}`); }); // signup function async function signupUser(req, res) { let username = req.body.username; let password = req.body.password; if (username && password) { try { // Check if user exists const exists = await isUserExists(username); if (exists) { console.log("User already exists"); return res.status(500).json({ message: "User already exists!" }); } // Hash password const salt = await bcrypt.genSalt(saltRounds); const hash = await bcrypt.hash(password, salt); // Insert user await insertUser(username, hash); return res.status(200).json({ message: "Account successfully created" }); } catch (err) { console.error("Error inserting data:", err); return res.status(500).json({ message: "Error inserting data" }); } } else { res.status(400).json({ message: "Form is empty" }); } } // login function async function loginUser(req, res) { let username = req.body.username; let password = req.body.password; if (username && password) { try { username = username.trim(); password = password.trim(); const result = await db.query( "SELECT * FROM accounts WHERE username = $1", [username], ); // check if user exists if (result.rows.length > 0) { // Compare password const match = await bcrypt.compare(password, result.rows[0].password); if (!match) { res.status(401).json({ message: "Invalid password" }); return; } const token = jwt.sign({ username }, jwtSecret, { expiresIn: "30d", // token expires in 30 days }); res.cookie("token", token, { httpOnly: true, maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days }); req.session.loggedin = true; req.session.username = username; res.status(200).json({ message: "Successfully logged in" }); } else { return res .status(401) .json({ message: "Incorrect Username or Password!" }); } } catch (error) { console.error("Error executing query", error); res.status(500).json({ message: "Internal server error" }); } } else { res.status(400).json({ message: "Please enter Username and Password!" }); } res.end(); }