2024-03-12 01:10:59 -06:00
// *****************************************************
// <!-- Section 1 : Import Dependencies -->
// *****************************************************
const express = require ( 'express' ) ; // To build an application server or API
const app = express ( ) ;
const handlebars = require ( 'express-handlebars' ) ;
const Handlebars = require ( 'handlebars' ) ;
const path = require ( 'path' ) ;
const pgp = require ( 'pg-promise' ) ( ) ; // To connect to the Postgres DB from the node server
const bodyParser = require ( 'body-parser' ) ;
const session = require ( 'express-session' ) ; // To set the session object. To store or access session data, use the `req.session`, which is (generally) serialized as JSON by the store.
2024-03-15 03:21:45 -06:00
const bcrypt = require ( 'bcryptjs' ) ; // To hash passwords
2024-03-12 01:10:59 -06:00
const axios = require ( 'axios' ) ; // To make HTTP requests from our server. We'll learn more about it in Part C.
2024-04-03 22:05:41 -06:00
const moment = require ( 'moment' ) ; // To extract current time data
2024-03-12 01:10:59 -06:00
// *****************************************************
// <!-- Section 2 : Connect to DB -->
// *****************************************************
// create `ExpressHandlebars` instance and configure the layouts and partials dir.
const hbs = handlebars . create ( {
extname : 'hbs' ,
2024-04-30 21:19:35 -06:00
layoutsDir : _ _dirname + '/client/src/views/layouts' ,
partialsDir : _ _dirname + '/client/src/views/partials' ,
2024-03-12 01:10:59 -06:00
} ) ;
// database configuration
const dbConfig = {
host : 'db' , // the database server
2024-03-12 01:56:44 -06:00
port : 5432 , // the database port
2024-03-12 01:10:59 -06:00
database : process . env . POSTGRES _DB , // the database name
user : process . env . POSTGRES _USER , // the user account to connect with
password : process . env . POSTGRES _PASSWORD , // the password of the user account
} ;
const db = pgp ( dbConfig ) ;
// test your database
db . connect ( )
. then ( obj => {
console . log ( 'Database connection successful' ) ; // you can view this message in the docker compose logs
obj . done ( ) ; // success, release the connection;
} )
. catch ( error => {
console . log ( 'ERROR:' , error . message || error ) ;
} ) ;
// *****************************************************
// <!-- Section 3 : App Settings -->
// *****************************************************
// Register `hbs` as our view engine using its bound `engine()` function.
app . engine ( 'hbs' , hbs . engine ) ;
app . set ( 'view engine' , 'hbs' ) ;
2024-04-30 21:19:35 -06:00
app . set ( 'views' , path . join ( _ _dirname , '/client/src/views' ) ) ;
2024-03-12 01:10:59 -06:00
app . use ( bodyParser . json ( ) ) ; // specify the usage of JSON for parsing request body.
// initialize session variables
2024-04-10 21:53:51 -06:00
app . get ( '/welcome' , ( req , res ) => {
res . json ( { status : 'success' , message : 'Welcome!' } ) ;
} ) ;
2024-03-12 01:10:59 -06:00
app . use (
session ( {
secret : process . env . SESSION _SECRET ,
saveUninitialized : false ,
resave : false ,
} )
) ;
app . use (
bodyParser . urlencoded ( {
extended : true ,
} )
) ;
2024-04-19 19:19:42 -06:00
app . use ( async function ( req , res , next ) {
res . locals . user = req . session . user ;
if ( res . locals . user ) {
try {
res . locals . fav _teams = await getFavoriteTeamsForUser ( res . locals . user . userid ) ;
} catch ( error ) {
console . error ( 'Error fetching favorite teams:' , error ) ;
}
}
next ( ) ;
} ) ;
2024-03-12 01:10:59 -06:00
// Serve static files from the 'public' directory
2024-04-30 21:19:35 -06:00
app . use ( express . static ( path . join ( _ _dirname , '/client/src/assets' ) ) ) ;
2024-03-12 01:10:59 -06:00
// *****************************************************
2024-04-13 14:25:34 -06:00
// <!-- Section 4 : Middleware -->
2024-03-12 01:10:59 -06:00
// *****************************************************
2024-04-13 18:22:04 -06:00
// Middleware to automatically update live scoreboard
2024-04-30 21:19:35 -06:00
const fetchMatchesData = require ( './client/src/assets/middleware/navigation-bar/current-match-information' ) ;
2024-04-13 14:25:34 -06:00
app . use ( fetchMatchesData ) ;
2024-04-13 18:22:04 -06:00
//Middleware to automatically update in-game time abbreviations
2024-04-13 19:33:30 -06:00
2024-04-30 21:19:35 -06:00
const convert _time = require ( './client/src/assets/middleware/navigation-bar/convert-time' ) ;
2024-04-04 22:16:16 -06:00
app . use ( convert _time ) ;
2024-04-13 15:09:24 -06:00
2024-04-14 12:10:29 -06:00
// Leagues Page Middleware
2024-04-30 21:19:35 -06:00
const fetchLeaguesData = require ( './client/src/assets/middleware/leagues-page/get-current-league-information' ) ;
const fetchLeagueScorerData = require ( './client/src/assets/middleware/leagues-page/get-current-league-top-scorers' ) ;
2024-04-13 19:39:36 -06:00
app . get ( '/league/:leagueID' , [ fetchLeaguesData , fetchLeagueScorerData ] , ( req , res ) => {
// Render the Handlebars view with league data
2024-04-14 12:10:29 -06:00
res . render ( 'pages/leagues-page' , {
2024-04-13 19:39:36 -06:00
leagueID : req . params . leagueID ,
leagues : res . locals . leagues ,
scorers : res . locals . topScorers // Assuming fetchLeagueScorerData sets the data in res.locals.scorers
} ) ;
2024-04-13 15:09:24 -06:00
} ) ;
2024-04-13 14:25:34 -06:00
2024-04-14 12:10:29 -06:00
// Clubs Page Middleware
2024-04-30 21:19:35 -06:00
const fetchClubsData = require ( './client/src/assets/middleware/clubs-page/get-current-club-information' ) ;
2024-04-14 12:10:29 -06:00
2024-04-21 14:11:35 -06:00
app . get ( '/club/:clubID' , [ fetchClubsData ] , ( req , res ) => {
2024-04-14 12:10:29 -06:00
// Render the Handlebars view with league data
2024-04-21 14:11:35 -06:00
var isFav = false ;
var fav _teams = res . locals . fav _teams ;
if ( res . locals . user && fav _teams )
{
2024-04-21 23:03:51 -06:00
const isTeamIDInFavTeams = fav _teams . some ( team => {
const teamIdInt = parseInt ( team . teamid ) ;
const clubIdInt = parseInt ( req . params . clubID ) ;
console . log ( 'Checking team:' , teamIdInt ) ;
console . log ( 'equal to' , clubIdInt ) ;
return teamIdInt === clubIdInt ;
} ) ;
2024-04-21 14:11:35 -06:00
if ( isTeamIDInFavTeams ) {
isFav = true
}
}
2024-04-14 12:10:29 -06:00
res . render ( 'pages/clubs-page' , {
2024-04-21 14:11:35 -06:00
isFav : isFav ,
2024-04-14 12:10:29 -06:00
clubID : req . params . clubID ,
2024-04-14 12:49:37 -06:00
clubs : res . locals . club
2024-04-14 12:10:29 -06:00
} ) ;
} ) ;
2024-04-13 14:25:34 -06:00
// *****************************************************
// <!-- Section 5 : API Routes -->
// *****************************************************
2024-03-12 01:56:44 -06:00
/ * * * * * * * * * * * * * * * * * * * * * * * *
Login Page Routes
* * * * * * * * * * * * * * * * * * * * * * * * * /
// Redirect to the /login endpoint
app . get ( '/' , ( req , res ) => {
2024-04-10 01:58:37 -06:00
res . redirect ( '/home' ) ;
2024-04-13 14:25:34 -06:00
} ) ;
2024-03-12 01:56:44 -06:00
2024-04-13 14:25:34 -06:00
// Render login page for /login route
app . get ( '/login' , ( req , res ) => {
2024-04-24 18:46:31 -06:00
res . render ( '/' ) ;
2024-04-13 14:25:34 -06:00
} ) ;
2024-03-12 01:56:44 -06:00
2024-04-13 14:25:34 -06:00
// Trigger login form to check database for matching username and password
app . post ( '/login' , async ( req , res ) => {
2024-03-12 01:56:44 -06:00
try {
2024-04-13 14:25:34 -06:00
// Check if username exists in DB
const user = await db . oneOrNone ( 'SELECT * FROM users WHERE username = $1' , req . body . username ) ;
2024-03-12 01:56:44 -06:00
2024-04-13 14:25:34 -06:00
if ( ! user ) {
// Redirect user to login screen if no user is found with the provided username
return res . redirect ( '/register' ) ;
}
2024-03-12 01:56:44 -06:00
2024-04-13 14:25:34 -06:00
// Check if password from request matches with password in DB
const match = await bcrypt . compare ( req . body . password , user . password ) ;
2024-03-12 01:56:44 -06:00
2024-04-13 14:25:34 -06:00
// Check if match returns no data
if ( ! match ) {
// Render the login page with the message parameter
2024-04-24 18:46:31 -06:00
return res . render ( '/' , { message : 'Password does not match' } ) ;
2024-04-13 14:25:34 -06:00
}
2024-04-17 18:06:45 -06:00
else {
2024-04-13 14:25:34 -06:00
// Save user information in the session variable
req . session . user = user ;
req . session . save ( ) ;
2024-03-12 01:56:44 -06:00
2024-04-13 14:25:34 -06:00
// Redirect user to the home page
2024-04-24 18:46:31 -06:00
res . redirect ( '/' ) ;
2024-04-17 18:06:45 -06:00
}
2024-03-12 01:56:44 -06:00
} catch ( error ) {
2024-04-13 14:25:34 -06:00
// Direct user to login screen if no user is found with matching password
res . redirect ( '/register' ) ;
2024-03-12 01:56:44 -06:00
}
2024-04-13 14:25:34 -06:00
} ) ;
2024-03-12 01:56:44 -06:00
2024-04-13 14:25:34 -06:00
/ * * * * * * * * * * * * * * * * * * * * * * * *
Registration Page Routes
* * * * * * * * * * * * * * * * * * * * * * * * * /
2024-03-12 01:56:44 -06:00
2024-04-13 14:25:34 -06:00
// Render registration page for /register route
app . get ( '/register' , ( req , res ) => {
2024-04-24 18:46:31 -06:00
res . redirect ( '/' ) ;
2024-04-13 14:25:34 -06:00
} ) ;
2024-04-10 23:09:30 -06:00
// Trigger Registration Form to Post
app . post ( '/register' , async ( req , res ) => {
2024-04-17 21:45:27 -06:00
try {
if ( ! req . body . username || ! req . body . password ) {
// If username or password is missing, respond with status 400 and an error message
return res . status ( 400 ) . json ( { status : 'error' , message : 'Invalid input' } ) ;
}
// Check if the username already exists in the database
const existingUser = await db . oneOrNone ( 'SELECT * FROM users WHERE username = $1' , req . body . username ) ;
if ( existingUser ) {
// If a user with the same username already exists, respond with status 409 and an error message
return res . status ( 409 ) . json ( { status : 'error' , message : 'Username already exists' } ) ;
}
// Hash the password using bcrypt library
const hash = await bcrypt . hash ( req . body . password , 10 ) ;
// Insert username and hashed password into the 'users' table
await db . none ( 'INSERT INTO users (username, password) VALUES ($1, $2)' , [ req . body . username , hash ] ) ;
const user = await db . oneOrNone ( 'SELECT * FROM users WHERE username = $1' , req . body . username ) ;
req . session . user = user ;
req . session . save ( ) ;
// Redirect user to the home page
res . redirect ( '/home' ) ;
} catch ( error ) {
// If an error occurs during registration, respond with status 500 and an error message
res . status ( 500 ) . json ( { status : 'error' , message : 'An error occurred during registration' } ) ;
}
2024-04-10 23:09:30 -06:00
} ) ;
2024-04-11 03:51:43 -06:00
/ * * * * * * * * * * * * * * * * * * * * * * * *
Home Page Routes
* * * * * * * * * * * * * * * * * * * * * * * * * /
2024-04-03 22:05:41 -06:00
2024-04-11 03:51:43 -06:00
app . get ( '/home' , ( req , res ) => {
2024-04-17 18:06:45 -06:00
const loggedIn = req . session . user ? true : false ;
2024-04-19 19:19:42 -06:00
res . render ( 'pages/home' ) ;
2024-04-11 03:51:43 -06:00
} ) ;
2024-04-23 23:11:21 -06:00
2024-04-22 17:26:10 -06:00
app . get ( '/logout' , ( req , res ) => {
req . session . destroy ( err => {
if ( err ) {
console . error ( 'Error destroying session:' , err ) ;
res . status ( 500 ) . send ( 'Internal Server Error' ) ;
} else {
2024-04-23 23:11:21 -06:00
// Redirect to the same page after destroying the session
res . redirect ( '/' ) ; // You can change '/' to the desired page if it's not the home page
2024-04-22 17:26:10 -06:00
}
} ) ;
} ) ;
2024-04-11 03:51:43 -06:00
2024-04-13 15:09:24 -06:00
/ * * * * * * * * * * * * * * * * * * * * * * * *
League Page Routes
* * * * * * * * * * * * * * * * * * * * * * * * * /
// Import and call generateLeagueRoutes function
2024-04-30 21:19:35 -06:00
const generateLeagueRoutes = require ( './client/src/assets/routes/league-pages/generate-league-routes' ) ;
2024-04-13 15:09:24 -06:00
generateLeagueRoutes ( app ) ;
2024-04-14 12:49:37 -06:00
/ * * * * * * * * * * * * * * * * * * * * * * * *
Club Page Routes
* * * * * * * * * * * * * * * * * * * * * * * * * /
// Import and call generateLeagueRoutes function
2024-04-30 21:19:35 -06:00
const generateClubRoutes = require ( './client/src/assets/routes/club-pages/generate-club-routes' ) ;
2024-04-14 12:49:37 -06:00
generateClubRoutes ( app ) ;
2024-04-17 18:06:45 -06:00
/ * * * * * * * * * * * * * * * * * * * * * * * *
Favorite Team Database
* * * * * * * * * * * * * * * * * * * * * * * * * /
// Function to add a new row to the FavoriteTeams table
// database configuration
2024-04-21 14:11:35 -06:00
app . post ( '/favteam/add' , async ( req , res , next ) => {
2024-04-17 18:06:45 -06:00
try {
2024-04-21 14:11:35 -06:00
const { userID , teamID , teamName , teamLogo } = req . body ;
2024-04-19 19:19:42 -06:00
2024-04-21 14:11:35 -06:00
// Check if the user is logged in
if ( ! req . session . user ) {
return res . status ( 400 ) . json ( { message : 'Login or register to add a favorite team.' } ) ;
}
2024-04-17 18:06:45 -06:00
2024-04-21 14:11:35 -06:00
// Insert the new favorite team into the database
const query = {
text : 'INSERT INTO FavoriteTeams (UserID, TeamID, TeamName, TeamLogo) VALUES ($1, $2, $3, $4)' ,
values : [ userID , teamID , teamName , teamLogo ] ,
} ;
await db . none ( query ) ;
console . log ( 'New favorite team added successfully.' ) ;
return res . status ( 200 ) . json ( { message : 'New favorite team added successfully.' } ) ;
2024-04-17 18:06:45 -06:00
} catch ( error ) {
2024-04-21 14:11:35 -06:00
console . error ( 'Error adding favorite team:' , error ) ;
return res . status ( 500 ) . json ( { error : 'Error adding favorite team' } ) ;
2024-04-17 18:06:45 -06:00
}
} ) ;
2024-04-23 20:53:03 -06:00
2024-04-19 19:19:42 -06:00
app . post ( '/favteam/remove' , async ( req , res ) => {
try {
2024-04-22 11:39:37 -06:00
const { userID , teamID } = req . body ;
2024-04-19 19:19:42 -06:00
// Check if the team exists for the user
const existingTeam = await db . oneOrNone (
2024-04-22 11:39:37 -06:00
'SELECT * FROM FavoriteTeams WHERE UserID = $1 AND TeamID = $2' ,
[ userID , teamID ]
2024-04-19 19:19:42 -06:00
) ;
2024-04-21 23:03:51 -06:00
// If the team does not exist for the user, return a 404 error
2024-04-19 19:19:42 -06:00
if ( ! existingTeam ) {
2024-04-21 23:03:51 -06:00
return res . status ( 404 ) . json ( { message : 'This team is not in your favorites.' } ) ;
2024-04-19 19:19:42 -06:00
}
// Remove the favorite team from the database
await db . none (
2024-04-22 11:39:37 -06:00
'DELETE FROM FavoriteTeams WHERE UserID = $1 AND TeamID = $2' ,
[ userID , teamID ]
2024-04-19 19:19:42 -06:00
) ;
console . log ( 'Favorite team removed successfully.' ) ;
return res . status ( 200 ) . json ( { message : 'Favorite team removed successfully.' } ) ;
} catch ( error ) {
console . error ( 'Error removing favorite team:' , error ) ;
2024-04-21 23:03:51 -06:00
// If the error is a database error, return a 500 error
if ( error instanceof QueryResultError ) {
return res . status ( 500 ) . json ( { error : 'Database error occurred while removing favorite team' } ) ;
}
// If the error is a generic error, return a 400 error
return res . status ( 400 ) . json ( { error : 'Error occurred while removing favorite team' } ) ;
2024-04-19 19:19:42 -06:00
}
} ) ;
2024-04-21 23:03:51 -06:00
2024-04-19 19:19:42 -06:00
async function getFavoriteTeamsForUser ( userId ) {
try {
// Execute the SQL query
const favoriteTeams = await db . any ( `
SELECT * FROM FavoriteTeams
WHERE UserID = $1 ;
` , userId); ` a `
// Return the result
return favoriteTeams ;
} catch ( error ) {
console . error ( 'Error fetching favorite teams:' , error ) ;
throw error ; // Rethrow the error for handling at a higher level
}
}
2024-03-12 01:10:59 -06:00
// *****************************************************
// <!-- Section 5 : Start Server-->
// *****************************************************
// starting the server and keeping the connection open to listen for more requests
2024-04-10 21:53:51 -06:00
module . exports = app . listen ( 3000 ) ;
2024-03-12 01:10:59 -06:00
console . log ( 'Server is listening on port 3000' ) ;