You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

278 lines
7.6 KiB

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');
});