const express = require("express"); const sql = require("mssql"); const bcrypt = require("bcrypt"); const cors = require("cors"); const jwt = require("jsonwebtoken") const dotenv = require("dotenv") const serverPort = 3001; const dev = process.argv.length > 2 && process.argv[2] == "-dev"; const app = express(); app.use(express.json()); app.use((req, res, next) =>{ if (req.path.endsWith(".html")) { res.redirect(req.path.replace(".html", "")); return; } next(); }) app.use(express.static("public")) app.use(cors({ origin: [ "https://project.rochesterx.dev", "https://localhost:3001" ] })); let dbConfig = null; dbConfig = { user: "server", password: "TorusField1*", server: "mssql.rochesterx.dev", database: "ProjectTest", options: { trustServerCertificate: true } }; let pool = null; setupPool(); dotenv.config(); const JWT_SECRET = process.env.JWT_SECRET; if (!JWT_SECRET) { throw new Error("JWT_SECRET not set in environment."); } async function setupPool() { pool = await sql.connect(dbConfig); } app.post("/register", async (req, res) => { const { username, password, role } = req.body; try { const hash = await bcrypt.hash(password, 10); await pool.request() .input("username", sql.VarChar, username) .input("hash", sql.VarChar, hash) .input("role", sql.VarChar, role) .query("INSERT INTO Users (Username, PasswordHash, Role, CreatedAt) VALUES (@username, @hash, @role, SYSUTCDATETIME())"); res.send({ success: true, message: "User registered" }) } catch (err) { if (err.message.includes("Violation of UNIQUE KEY constraint")) { res.status(500).send({ success: false, message: `Username "${username}" is already taken.` }); } res.status(500).send({ success: false, message: err.message }); } }); app.post("/login", async (req, res) => { const { username, password } = req.body; try { const result = await pool.request() .input("username", sql.VarChar, username) .query("SELECT * FROM Users WHERE Username = @username"); if (result.recordset.length == 0) return res.status(400).send({ message: "User not found" }); const hash = result.recordset[0].PasswordHash; const isDeleted = result.recordset[0].IsDeleted; if (isDeleted === true) { return res.status(400).json({ message: "User not found (deleted)" }) } const match = await bcrypt.compare(password, hash); if (match){ const token = jwt.sign(result.recordset[0], JWT_SECRET, { expiresIn: "1h" }); res.send({ success: true, message: "Login successful", token }); await pool.request() .input("username", sql.VarChar, username) .query("UPDATE Users SET LastLogin = SYSUTCDATETIME() WHERE Username = @username"); console.log("Issued token: " + JSON.stringify(token)) } else res.status(401).send({ success: false, message: "Invalid credentials" }); } catch (err) { res.status(500).send({ success: false, message: err.message }); } }); app.post("/getPlayers", authenticate, async (req, res) => { const { player } = req.body; const result = await pool.request() .input("query", sql.VarChar, player) .query(`SELECT * FROM Player WHERE player_name LIKE '%' + @query + '%'`); res.status(200).json({ query: player, matches: result.recordset }); }); app.post("/getPlayer", authenticate, async (req, res) => { const { id } = req.body; const result = await pool.request() .input("query", sql.VarChar, id) .query(`SELECT * FROM Player WHERE player_id = @query`); if (result.recordset.length !== 1) { res.status(400).json({ success: false }) return; } res.status(200).json({ success: true, match: result.recordset[0] }); }); app.post("/getInfo", authenticate, async (req, res) => { const userData = req.user; res.status(200).json(userData); }); app.post("/getCourses", authenticate, async (req, res) => { const result = await pool.request() .query("SELECT * FROM Courses"); const courses = result.recordset; res.status(200).json(courses); }); app.post("/setInfo", authenticate, async (req, res) => { const { firstName, lastName, dob } = req.body; try { await pool.request() .input("username", sql.VarChar, req.user.Username) .input("firstName", sql.NVarChar(50), firstName) .input("lastName", sql.NVarChar(50), lastName) .input("dob", sql.Date, dob) .query(` UPDATE Users SET FirstName = @firstName, LastName = @lastName, DOB = @dob WHERE Username = @username `); } catch (error) { console.log(error); res.status(500).json({ message: "Update request failed" }) } var updatedUser = req.user; updatedUser.FirstName = firstName; updatedUser.LastName = lastName; updatedUser.DOB = dob; const token = jwt.sign(updatedUser, JWT_SECRET); console.log("Issued token: " + JSON.stringify(token)) res.status(200).send({ success: true, message: "Information updated successfully", token }); }); app.post("/delete", authenticate, async (req, res) => { let { username, actor } = req.body; if (username === true) { username = req.user.Username; } if (actor === true) { actor = req.user.Username; } console.log(`Deleting user ${username}`); await pool.request() .input("username", sql.VarChar, username) .input("actor", sql.VarChar, actor) .query("UPDATE Users SET IsDeleted = 1, DeletedAt = SYSUTCDATETIME(), DeletedBy = @actor WHERE Username = @username"); console.log(`User ${username} deleted`); res.status(200).json({ message: `User "${username}" deleted.` }); }); async function authenticate(req, res, next) { try { const authenticationHeader = req.headers["authorization"]; console.log("authenticationheader: " + authenticationHeader); const token = authenticationHeader.split(" ")[1]; console.log(JSON.stringify(authenticationHeader)); const decoded = jwt.verify(token, JWT_SECRET); req.user = decoded; console.log(req.user); console.log(decoded.Username + " authenticated"); } catch (error) { console.log("Authentication header missing"); console.log(error); res.status(401).json({ message: "You are not logged in", error: error, logout: true }); return; } next(); } app.get("/player/:id", (req, res) => { res.sendFile(__dirname + "/public/player.html"); }) app.get("/search", (req, res) => { res.sendFile(__dirname + "/public/search.html"); }) app.get("/home", (req, res) => { res.sendFile(__dirname + "/public/home.html"); }) app.get("/info", (req, res) => { res.sendFile(__dirname + "/public/info.html"); }) app.get("/register", (req, res) => { res.sendFile(__dirname + "/public/register.html"); }) app.get("/login", (req, res) => { res.sendFile(__dirname + "/public/login.html"); }) app.listen(serverPort, "0.0.0.0", () => console.log(`Running ${dev ? "dev " : ""}server on port ${serverPort}`));