2025-11-12 10:13:24 -05:00
const express = require ( "express" ) ;
const sql = require ( "mssql" ) ;
const bcrypt = require ( "bcrypt" ) ;
const cors = require ( "cors" ) ;
const jwt = require ( "jsonwebtoken" )
const dotenv = require ( "dotenv" )
2025-11-12 10:15:10 -05:00
const serverPort = 3001 ;
2025-11-12 10:13:24 -05:00
const dev = process . argv . length > 2 && process . argv [ 2 ] == "-dev" ;
const app = express ( ) ;
app . use ( express . json ( ) ) ;
2025-11-18 21:16:35 -05:00
app . use ( ( req , res , next ) => {
if ( req . path . endsWith ( ".html" ) ) {
res . redirect ( req . path . replace ( ".html" , "" ) ) ;
return ;
}
next ( ) ;
} )
2025-11-12 10:13:24 -05:00
app . use ( express . static ( "public" ) )
app . use ( cors ( {
origin : [
"https://project.rochesterx.dev" ,
2025-11-12 10:15:10 -05:00
"https://localhost:3001"
2025-11-12 10:13:24 -05:00
]
} ) ) ;
let dbConfig = null ;
dbConfig = {
user : "server" ,
password : "TorusField1*" ,
server : "mssql.rochesterx.dev" ,
2025-11-26 21:10:22 -05:00
database : "Project" ,
2025-11-12 10:13:24 -05:00
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 } ) ;
}
} ) ;
2025-11-26 21:10:22 -05:00
app . post ( "/getWatchlist" , authenticate , async ( req , res ) => {
const { id } = req . body ;
const watchlist = await pool . request ( )
. input ( "userID" , sql . Int , req . user . Id )
. input ( "id" , sql . Int , id )
. query ( ` SELECT Player.PlayerName, Player.Team, Player.Position, Player.PlayerID FROM Watch JOIN Player ON Watch.PlayerID = Player.PlayerID WHERE UserID = @userID ` ) ;
res . status ( 200 ) . json ( { watchlist : watchlist . recordset } ) ;
} ) ;
app . post ( "/isWatched" , authenticate , async ( req , res ) => {
const { id } = req . body ;
const watchlist = await pool . request ( )
. input ( "userID" , sql . Int , req . user . Id )
. input ( "id" , sql . Int , id )
. query ( ` SELECT PlayerID FROM Watch WHERE UserID = @userID ` ) ;
const isWatched = watchlist . recordset . some ( row => row . PlayerID === parseInt ( id ) ) ;
res . status ( 200 ) . json ( { isWatched : isWatched } ) ;
} ) ;
app . post ( "/toggleWatched" , authenticate , async ( req , res ) => {
const { id } = req . body ;
const watchlist = await pool . request ( )
. input ( "userID" , sql . Int , req . user . Id )
. input ( "id" , sql . Int , id )
. query ( ` SELECT PlayerID FROM Watch WHERE UserID = @userID ` ) ;
console . log ( watchlist . recordset ) ;
const isWatched = watchlist . recordset . some ( row => row . PlayerID === parseInt ( id ) ) ;
console . log ( isWatched ) ;
if ( isWatched ) {
const result = await pool . request ( )
. input ( "userID" , sql . Int , req . user . Id )
. input ( "id" , sql . Int , id )
. query ( ` DELETE FROM Watch WHERE UserID = @userID AND PlayerID = @id ` ) ;
res . status ( 200 ) . json ( { message : "No longer watching player" } ) ;
return ;
}
// Otherwise, watch the player
const result = await pool . request ( )
. input ( "userID" , sql . Int , req . user . Id )
. input ( "id" , sql . Int , id )
. query ( ` INSERT INTO Watch (UserID, PlayerID) VALUES (@userID, @id) ` ) ;
res . status ( 200 ) . json ( { message : "Watching Player" } ) ;
} ) ;
2025-11-17 18:56:31 -05:00
app . post ( "/getPlayers" , authenticate , async ( req , res ) => {
2025-11-26 22:52:05 -05:00
const { player , positions } = req . body ;
2025-11-12 10:13:24 -05:00
const result = await pool . request ( )
2025-11-17 18:56:31 -05:00
. input ( "query" , sql . VarChar , player )
2025-11-26 22:52:05 -05:00
. input ( "one" , sql . VarChar , positions [ 0 ] )
. input ( "two" , sql . VarChar , positions [ 1 ] )
. input ( "three" , sql . VarChar , positions [ 2 ] )
. input ( "four" , sql . VarChar , positions [ 3 ] )
. query ( ` SELECT p.PlayerID, p.PlayerName, c.TotalValue, p.Team, p.Position FROM Player AS p JOIN Contract AS c ON p.PlayerID = c.PlayerID WHERE p.PlayerName LIKE '%' + @query + '%' AND p.Position IN (@one, @two, @three, @four); ` ) ;
2025-11-12 10:13:24 -05:00
2025-11-17 18:56:31 -05:00
res . status ( 200 ) . json ( { query : player , matches : result . recordset } ) ;
2025-11-12 10:13:24 -05:00
} ) ;
2025-11-26 21:10:22 -05:00
app . post ( "/getHighest" , authenticate , async ( req , res ) => {
const { amount } = req . body ;
const result = await pool . request ( )
. input ( "amount" , sql . Int , amount )
. query ( `
SELECT TOP ( @ amount ) p . PlayerID , p . PlayerName , p . [ Position ] , p . Team , TotalValue
FROM Player AS p JOIN Contract AS c ON p . PlayerID = c . PlayerID
ORDER BY TotalValue DESC ;
` );
res . status ( 200 ) . json ( { matches : result . recordset } ) ;
} ) ;
2025-11-18 21:16:35 -05:00
app . post ( "/getPlayer" , authenticate , async ( req , res ) => {
const { id } = req . body ;
const result = await pool . request ( )
. input ( "query" , sql . VarChar , id )
2025-11-26 21:10:22 -05:00
. query ( ` SELECT p.PlayerName, p.PlayerID, c.TotalValue, p.Team, p.Position FROM Player AS p JOIN Contract AS c ON p.PlayerID = c.PlayerID WHERE p.PlayerID = @query ` ) ;
2025-11-18 21:16:35 -05:00
if ( result . recordset . length !== 1 ) {
res . status ( 400 ) . json ( { success : false } )
return ;
}
res . status ( 200 ) . json ( { success : true , match : result . recordset [ 0 ] } ) ;
} ) ;
2025-11-12 10:13:24 -05:00
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" } )
}
2025-11-18 21:16:35 -05:00
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
} ) ;
2025-11-12 10:13:24 -05:00
} ) ;
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" ] ;
2025-11-18 21:16:35 -05:00
console . log ( "authenticationheader: " + authenticationHeader ) ;
2025-11-12 10:13:24 -05:00
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" ) ;
2025-11-18 21:16:35 -05:00
console . log ( error ) ;
2025-11-12 10:13:24 -05:00
res . status ( 401 ) . json ( { message : "You are not logged in" , error : error , logout : true } ) ;
return ;
}
next ( ) ;
}
2025-11-18 21:16:35 -05:00
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" ) ;
} )
2025-11-12 10:13:24 -05:00
app . listen ( serverPort , "0.0.0.0" , ( ) => console . log ( ` Running ${ dev ? "dev " : "" } server on port ${ serverPort } ` ) ) ;