Browse Source

More upload safety checks and error reporting

new_auth
Quildra 2 years ago
parent
commit
f63e369dad
  1. 3
      packages/bridge-server/src/race-results/race-result.model.ts
  2. 11
      packages/bridge-server/src/race-results/race-results.service.ts
  3. 43
      packages/bridge-server/src/upload/upload.service.ts
  4. 12
      packages/bridge-ui/src/app/interceptors/snackbar.interceptor.ts

3
packages/bridge-server/src/race-results/race-result.model.ts

@ -23,4 +23,7 @@ export class RaceResult extends Model {
@Column
time: number;
@Column
replayHash: string;
}

11
packages/bridge-server/src/race-results/race-results.service.ts

@ -32,7 +32,13 @@ export class RaceResultsService {
});
}
async create(raceId: number, racerId: number, time: number, replayPath: string) {
async findOneByReplayHash(hash: string) {
return this.raceResultModel.findOne({
where: {replayHash: hash}
});
}
async create(raceId: number, racerId: number, time: number, replayPath: string, replayHash: string) {
try {
await this.sequelize.transaction( async t => {
const transactionHost = { transaction: t };
@ -40,7 +46,8 @@ export class RaceResultsService {
raceId: raceId,
racerId: racerId,
replayPath: replayPath,
time: time
time: time,
replayHash: replayHash
},
transactionHost
);

43
packages/bridge-server/src/upload/upload.service.ts

@ -1,4 +1,4 @@
import { HttpException, HttpStatus, Injectable, NotAcceptableException } from '@nestjs/common';
import { Injectable, NotAcceptableException } from '@nestjs/common';
import { RacersService } from 'src/racers/racers.service';
import { RaceResultsService } from 'src/race-results/race-results.service';
import { RacesService } from 'src/races/races.service';
@ -8,7 +8,9 @@ const AdvancableBuffer = require('../utilities/advanceable-buffer');
const gbxHeader = require('../trackmania-replays/gbx-header');
const gbxReplay = require('../trackmania-replays/gbx-replay');
const fs = require('fs');
import { createHash } from 'crypto';
import { readFileSync, unlinkSync } from 'fs';
@Injectable()
@ -23,52 +25,57 @@ export class UploadService {
{}
async replayUploaded(file: Express.Multer.File, body: any) {
console.log(file.path)
const hash = createHash('sha256');
let buffer = readFileSync(file.path);
hash.update(buffer);
let file_hash = hash.digest('hex');
console.log(file_hash);
let results = await this.raceResultsService.findOneByReplayHash(file_hash);
console.log(results);
if(results != undefined) {
unlinkSync(file.path);
throw new NotAcceptableException('AlreadyUploaded', {cause: new Error(), description: 'This replay has already been submitted.'});
}
let buffer = fs.readFileSync(file.path)
console.log(buffer)
let buff = new AdvancableBuffer(buffer);
let header = new gbxHeader();
header.parse(buff);
if (header.is_vaild == false)
{
return;
unlinkSync(file.path);
throw new NotAcceptableException('GBXInvalid', {cause: new Error(), description: 'This is not a valid replay file'});
}
console.log(header);
let replay = new gbxReplay();
replay.parse(buff);
let currentRacer = await this.racersService.findOrCreate(replay.gamerHandle);
console.log(currentRacer);
let race = await this.racesService.findOneBySeasonAndMapUID(body.seasonId, replay.mapUID)
console.log(race);
if(race == undefined) {
console.log("No race for " + replay.mapUID);
throw new NotAcceptableException('RaceNotFound', {cause: new Error(), description: 'This replay is not for a valid race.'})
}
if(race.endDate.getTime() < Date.now()) {
throw new NotAcceptableException('RaceClosed', {cause: new Error(), description: 'Race closed for new entries.'})
console.log("Too Late!")
return;
}
if(Date.now() < race.startDate.getTime()) {
throw new NotAcceptableException('RaceNotOpen', {cause: new Error(), description: 'Race not yet open for entries.'})
console.log("Too Soon!")
return;
}
/*
if(body.fileTime < race.startDate.getTime()) {
throw new NotAcceptableException('EarlyReplay', {cause: new Error(), description: 'Replay generated before the race started.'})
console.log("Race Too Soon!")
return;
}
*/
let result = await this.raceResultsService.create(race.id, currentRacer.id, replay.bestTime, file.path);
console.log(result);
await this.raceResultsService.create(race.id, currentRacer.id, replay.bestTime, file.path, file_hash);
this.seasonStandingsService.updateStandings(body.seasonId);
}

12
packages/bridge-ui/src/app/interceptors/snackbar.interceptor.ts

@ -35,10 +35,22 @@ export class SnackbarInterceptor implements HttpInterceptor {
message = "Upload Failed: Race not open for uploads yet.";
break;
}
case "RaceNotFound": {
message = "Upload Failed: Replay reports to be for an race that doesn't exist";
break;
}
case "EarlyReplay": {
message = "Upload Failed: Replay made before race open.";
break;
}
case 'AlreadyUploaded':{
message = "Upload Rejected: Replay has already been submitted.";
break;
}
case 'GBXInvalid':{
message = "Upload Rejected: Replay is not a valid GBX file.";
break;
}
}
this.snackBar.open(message, 'dismiss', { panelClass: ['errorSnack'], verticalPosition: 'top'});
return throwError(resp);

Loading…
Cancel
Save