const express = require('express'); const bodyParser = require('body-parser'); const jwt = require('jsonwebtoken'); const cors = require('cors'); const { Document, Schema, connect } = require('camo'); // Define the Racers schema class Racer extends Document { constructor() { super(); this.name = String; this.gameHandle = String; } } // Define the Seasons schema class Season extends Document { constructor() { super(); this.title = String; this.subTitle = String; this.startDate = String; } } class SeasonRacer extends Document{ constructor() { super(); this.racer = Racer; this.seasion = Season; } } // Define the Races schema class Race extends Document { constructor() { super(); this.weekNumber = Number; this.startDate = Date; this.endDate = Date; this.mapName = String; this.mapThumbnailUrl = String; this.mapUID = String; this.season = Season; } } // Define the RaceResults schema class RaceResult extends Document { constructor() { super(); this.race = Race; this.racer = Racer; this.timeInMilliseconds = Number; this.replayPath = String; } } connect('nedb://./nedb-database'); const fs = require('fs'); const app = express(); const upload = require('./backend/routes/upload-replay'); const TMIO = require('trackmania.io'), client = new TMIO.Client(); const AdvancableBuffer = require('./backend/utilities/AdvancableBuffer.js'); const gbxHeader = require('./backend/trackmania-replays/gbx-header.js'); const gbxReplay = require('./backend/trackmania-replays/gbx-replay'); // handling CORS app.use(cors()); app.use(bodyParser.json()); //const RSA_PRIVATE_KEY = fs.readFileSync('./private.key'); const RSA_PRIVATE_KEY = "Secret_KeY"; const RSA_PUBLIC_KEY = fs.readFileSync('./public.key'); const users = [ { id: 1, username: 'user1', password: 'password1' }, { id: 2, username: 'user2', password: 'password2' } ]; function verifyToken(req, res, next) { const token = req.headers['authorization']; console.log(token); if (!token || !token.startsWith('Bearer ')) { return res.status(401).json({ message: 'Unauthorized' }); } const tokenWithoutBearer = token.slice(7); // Remove 'Bearer ' prefix jwt.verify(tokenWithoutBearer, RSA_PRIVATE_KEY, (err, decoded) => { if (err) { console.error(err); return res.status(401).json({ message: 'Invalid token' }); } req.user = decoded.user; next(); }); } app.post('/api/login', (req, res) => { const { username, password } = req.body; // Check if the user exists and the password is correct const user = users.find(u => u.username === username && u.password === password); if (!user) { return res.status(401).json({ message: 'Invalid username or password' }); } // Generate and return a JWT token const token = jwt.sign({ userId: user.id }, RSA_PRIVATE_KEY, { expiresIn: '1h', algorithm: 'HS256' }); console.log(token); res.json({ token }); }); app.get('/api/seasons', async (req, res) => { const seasons = await Season.find(); res.json({seasons: seasons}); }); function remapRaceInfo(raceRecord) { modableRaceObject = { weekNumber: raceRecord.weekNumber, startDate: raceRecord.startDate, endDate: raceRecord.endDate, mapName: raceRecord.mapName, mapThumbnailUrl: raceRecord.mapThumbnailUrl, mapUID: raceRecord.mapUID, season: raceRecord.season, entries: [] } return modableRaceObject; } app.get('/api/seasons/:id', async (req, res) => { const { id } = req.params; try { let races = [] let racersInSeason = []; const seasonDetails = await Season.findOne({_id: id}); const seasonRaceDetails = await Race.find({season: id}); for (let i = 0; i < seasonRaceDetails.length; i++) { let seasonRace = seasonRaceDetails[i]; let modableRace = remapRaceInfo(seasonRace); const fastestTimesMap = new Map(); const raceResults = await RaceResult.find({race: seasonRace._id}); raceResults.forEach(result => { const racerId = result.racer._id; const timeInMilliseconds = result.timeInMilliseconds; if (!fastestTimesMap.has(racerId) || timeInMilliseconds < fastestTimesMap.get(racerId)) { fastestTimesMap.set(racerId, {"racer":result.racer, "timeInMilliseconds":timeInMilliseconds}); } }); fastestTimesMap.forEach((values, key) =>{ modableRace.entries.push({"racer":values.racer, "timeInMilliseconds":values.timeInMilliseconds, "position":0}) }) //modableRace.entries = Array.from(fastestTimesMap.entries()).map(([racerId, timeInMilliseconds]) => ({ // racerId, // timeInMilliseconds //})); // This is wrong it will get only the racers the in last race it processes. racersInSeason = Array.from(new Set(raceResults.map(result => result.racer))); races.push(modableRace); } let i = 1; let standings = [] racersInSeason.forEach((racer) => { let entry = {} entry._id = i; entry.position = i; entry.points = 100-i; entry.user = racer; standings.push(entry); }); res.json({season: seasonDetails, races: races, standings: standings}); } catch( error ) { console.error(error) res.status(500).send('Internal Server Error'); } }); app.post('/api/seasons/add', verifyToken, async (req, res) => { const { title, subTitle, startDate } = req.body; const season = await Season.create({ title, subTitle, startDate }).save(); console.log(season); res.json(season); }); app.post('/api/race/add', verifyToken, async (req, res) => { const { seasonId, startDate, endDate, mapUID, weekNumber } = req.body; try{ client.maps.get(mapUID).then(async mapInfo => { console.log(mapInfo.name); const season = await Season.findOne({_id: seasonId}); const mapName = mapInfo.name; const mapThumbnailUrl = mapInfo.thumbnail; const race = await Race.create({ weekNumber, startDate, endDate, mapName, mapUID, mapThumbnailUrl, season }) console.log(race); race.save(); res.json(race); }); } catch (error) { console.error(error) res.status(500).send('Internal Server Error'); } }); app.post('/api/racer/add', verifyToken, async (req, res) => { const { name, gamerHandle, seasonId } = req.body; const season = await Season.findOne({_id: seasonId}); let existingRacer = await Racer.findOne({ gameHandle: gamerHandle }); if (existingRacer == undefined) { existingRacer = await Racer.create({ name, gamerHandle }); existingRacer.save(); } }); // route for handling requests from the Angular client app.post('/api/upload-replay', upload.single('file'), async (req, res) => { let file = req.file; const { seasonId } = req.body; console.log("File uploaded: ", req.file); await fs.readFile(file.path, async function(err, buffer) { buff = new AdvancableBuffer(buffer); header = new gbxHeader(); header.parse(buff); if (header.is_vaild == false) { return; } console.log(header); replay = new gbxReplay(); replay.parse(buff); let currentRacer = await Racer.findOne({gameHandle: replay.gamerHandle}); console.log(currentRacer); if (currentRacer == undefined) { currentRacer = await Racer.create({name: "DanH", gameHandle: replay.gamerHandle}); currentRacer.save(); } let race = await Race.findOne({season: seasonId, mapUID: replay.mapUID}) result = await RaceResult.create({race: race, racer: currentRacer, timeInMilliseconds: replay.bestTime, replayPath: file.destination}); result.save(); res.status(200); }); }); app.listen(3000, () => { console.log('Server listening on port 3000'); });