register match working
This commit is contained in:
+1
-1
Submodule src/db updated: 491213147e...c985feb726
@@ -1,291 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { Router } from 'express'
|
||||
import { body, header } from 'express-validator'
|
||||
import { /* CreateKillRecord, */ CheckServerToken } from '../db/db'
|
||||
import { validateErrors } from '../common'
|
||||
|
||||
const router = Router()
|
||||
|
||||
// auth middleware
|
||||
router.post(
|
||||
'/*',
|
||||
header('authorization')
|
||||
.exists({ checkFalsy: true })
|
||||
.withMessage('Missing Authorization Header')
|
||||
.bail()
|
||||
.custom((e) => e.split(' ')[0].toLowerCase() === 'bearer')
|
||||
.withMessage('Authorization Token is not Bearer'),
|
||||
validateErrors,
|
||||
(req, res, next) => {
|
||||
void (async () => {
|
||||
if (!req) return res.sendStatus(500)
|
||||
if (!req.headers.authorization) {
|
||||
console.error('no authorization header')
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
const query = await CheckServerToken(
|
||||
req.headers.authorization.split(' ')[1]
|
||||
)
|
||||
if (!query?.host_id) {
|
||||
console.error(
|
||||
`incorrect token : ${
|
||||
req.headers.authorization.split(' ')[1]
|
||||
} with IP ${
|
||||
req.headers['x-forwarded-for']?.toString() ??
|
||||
req.socket.remoteAddress?.toString() ??
|
||||
''
|
||||
}`
|
||||
)
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
next()
|
||||
})()
|
||||
}
|
||||
)
|
||||
|
||||
// Route to check auth
|
||||
router.post('/', (req, res) => {
|
||||
res.sendStatus(200)
|
||||
})
|
||||
|
||||
// const serversCount: Record<string, number> = {}
|
||||
// const serversTimeout: Record<string, NodeJS.Timeout> = {}
|
||||
|
||||
// same rate limiting code as register. max 10 kills per server every 1 sec. should be enough.
|
||||
/* router.post('/kill', (req, res, next) => {
|
||||
let host = Number(req.query.serverId)
|
||||
if (serversCount[serverId] > 2) {
|
||||
return res.status(429).json({
|
||||
error: 'too many requests. Are players really making that much kills ?'
|
||||
})
|
||||
}
|
||||
clearTimeout(serversTimeout[serverId])
|
||||
serversCount[serverId] =
|
||||
serversCount[serverId] ?? (serversCount[serverId] + 1) | 1
|
||||
serversTimeout[serverId] = setTimeout(() => {
|
||||
serversCount[serverId] = 0
|
||||
}, 1000)
|
||||
next()
|
||||
}) */
|
||||
|
||||
router.post(
|
||||
'/kill',
|
||||
body([
|
||||
'attacker_current_weapon_mods',
|
||||
'attacker_weapon_1_mods',
|
||||
'attacker_weapon_2_mods',
|
||||
'attacker_weapon_3_mods',
|
||||
'attacker_offhand_weapon_1_mods',
|
||||
'attacker_offhand_weapon_2_mods',
|
||||
'victim_current_weapon_mods',
|
||||
'victim_weapon_1_mods',
|
||||
'victim_weapon_2_mods',
|
||||
'victim_weapon_3_mods',
|
||||
'victim_offhand_weapon_1_mods',
|
||||
'victim_offhand_weapon_2_mods'
|
||||
]).customSanitizer((value) => {
|
||||
if (isNaN(value) || !value) {
|
||||
value = 0
|
||||
}
|
||||
return value
|
||||
}),
|
||||
body([
|
||||
'attacker_current_weapon_mods',
|
||||
'attacker_weapon_1_mods',
|
||||
'attacker_weapon_2_mods',
|
||||
'attacker_weapon_3_mods',
|
||||
'attacker_offhand_weapon_1_mods',
|
||||
'attacker_offhand_weapon_2_mods',
|
||||
'victim_current_weapon_mods',
|
||||
'victim_weapon_1_mods',
|
||||
'victim_weapon_2_mods',
|
||||
'victim_weapon_3_mods',
|
||||
'victim_offhand_weapon_1_mods',
|
||||
'victim_offhand_weapon_2_mods'
|
||||
])
|
||||
.toInt()
|
||||
.isInt()
|
||||
.withMessage('must be a valid int'),
|
||||
body(['distance', 'player_count'])
|
||||
.toInt()
|
||||
.isInt()
|
||||
.withMessage('must be a valid int'),
|
||||
body(['distance', 'game_time'])
|
||||
.toFloat()
|
||||
.isFloat()
|
||||
.withMessage('must be a valid float'),
|
||||
body(
|
||||
[
|
||||
'attacker_id',
|
||||
'victim_id',
|
||||
'killstat_version',
|
||||
'match_id',
|
||||
'game_mode',
|
||||
'map',
|
||||
'attacker_name',
|
||||
'attacker_current_weapon',
|
||||
'attacker_weapon_1',
|
||||
'attacker_weapon_2',
|
||||
'attacker_weapon_3',
|
||||
'attacker_offhand_weapon_1',
|
||||
'attacker_offhand_weapon_2',
|
||||
'victim_name',
|
||||
'victim_current_weapon',
|
||||
'victim_weapon_1',
|
||||
'victim_weapon_2',
|
||||
'victim_weapon_3',
|
||||
'victim_offhand_weapon_1',
|
||||
'victim_offhand_weapon_2',
|
||||
'cause_of_death',
|
||||
'victim_titan',
|
||||
'attacker_titan'
|
||||
],
|
||||
'must be composed of a maximum of 50 valid ascii characters'
|
||||
)
|
||||
.isString()
|
||||
.isLength({ max: 50 })
|
||||
.isAscii(),
|
||||
body('servername').isString().isLength({ max: 100 }).isAscii(),
|
||||
body(['distance', 'game_time'], 'must be postitive floats').isFloat({
|
||||
min: 0
|
||||
}),
|
||||
body(['cause_of_death', 'victim_id'], 'mandatory').exists().notEmpty(),
|
||||
// do we need this ?
|
||||
// body('servername').customSanitizer(e => e.replace(/[^a-z0-9]/gi, '')),
|
||||
validateErrors,
|
||||
(req, res) => {
|
||||
void (async () => {
|
||||
// Do we check the same thing twice ?????
|
||||
if (!req.headers.authorization) return res.sendStatus(403)
|
||||
const headers = req.headers.authorization.split(' ')
|
||||
if (headers[0].toLowerCase() !== 'bearer') { return res.status(403).send('authorization must be token bearer') }
|
||||
const query = await CheckServerToken(headers[1])
|
||||
if (!query) return res.sendStatus(403)
|
||||
// const host = query.id
|
||||
// const {
|
||||
// servername,
|
||||
// match_id,
|
||||
// game_mode,
|
||||
// map,
|
||||
// game_time,
|
||||
// player_count,
|
||||
// attacker_name,
|
||||
// attacker_id,
|
||||
// attacker_current_weapon,
|
||||
// attacker_current_weapon_mods,
|
||||
// attacker_weapon_1,
|
||||
// attacker_weapon_1_mods,
|
||||
// attacker_weapon_2,
|
||||
// attacker_weapon_2_mods,
|
||||
// attacker_weapon_3,
|
||||
// attacker_weapon_3_mods,
|
||||
// attacker_offhand_weapon_1,
|
||||
// attacker_offhand_weapon_1_mods,
|
||||
// attacker_offhand_weapon_2,
|
||||
// attacker_offhand_weapon_2_mods,
|
||||
// victim_name,
|
||||
// victim_id,
|
||||
// victim_current_weapon,
|
||||
// victim_current_weapon_mods,
|
||||
// victim_weapon_1,
|
||||
// victim_weapon_1_mods,
|
||||
// victim_weapon_2,
|
||||
// victim_weapon_2_mods,
|
||||
// victim_weapon_3,
|
||||
// victim_weapon_3_mods,
|
||||
// victim_offhand_weapon_1,
|
||||
// victim_offhand_weapon_1_mods,
|
||||
// victim_offhand_weapon_2,
|
||||
// victim_offhand_weapon_2_mods,
|
||||
// cause_of_death,
|
||||
// distance
|
||||
// } = req.body
|
||||
// CreateKillRecord({
|
||||
// servername,
|
||||
// host,
|
||||
// match_id,
|
||||
// game_mode,
|
||||
// map,
|
||||
// game_time,
|
||||
// player_count,
|
||||
// attacker_name,
|
||||
// attacker_id,
|
||||
// attacker_current_weapon,
|
||||
// attacker_current_weapon_mods,
|
||||
// attacker_weapon_1,
|
||||
// attacker_weapon_1_mods,
|
||||
// attacker_weapon_2,
|
||||
// attacker_weapon_2_mods,
|
||||
// attacker_weapon_3,
|
||||
// attacker_weapon_3_mods,
|
||||
// attacker_offhand_weapon_1,
|
||||
// attacker_offhand_weapon_1_mods,
|
||||
// attacker_offhand_weapon_2,
|
||||
// attacker_offhand_weapon_2_mods,
|
||||
// victim_name,
|
||||
// victim_id,
|
||||
// victim_current_weapon,
|
||||
// victim_current_weapon_mods,
|
||||
// victim_weapon_1,
|
||||
// victim_weapon_1_mods,
|
||||
// victim_weapon_2,
|
||||
// victim_weapon_2_mods,
|
||||
// victim_weapon_3,
|
||||
// victim_weapon_3_mods,
|
||||
// victim_offhand_weapon_1,
|
||||
// victim_offhand_weapon_1_mods,
|
||||
// victim_offhand_weapon_2,
|
||||
// victim_offhand_weapon_2_mods,
|
||||
// cause_of_death,
|
||||
// distance
|
||||
// })
|
||||
// .then((e) => {
|
||||
// res.sendStatus(201)
|
||||
// console.log(
|
||||
// `[${new Date().toLocaleString()}] Kill submitted for server ${servername}, ${attacker_name} killed ${victim_name}`
|
||||
// )
|
||||
// })
|
||||
// .catch((e) => {
|
||||
// res.sendStatus(500)
|
||||
// console.error({
|
||||
// killstat_version,
|
||||
// servername,
|
||||
// host,
|
||||
// match_id,
|
||||
// game_mode,
|
||||
// map,
|
||||
// game_time,
|
||||
// player_count,
|
||||
// attacker_name,
|
||||
// attacker_id,
|
||||
// attacker_current_weapon,
|
||||
// attacker_current_weapon_mods,
|
||||
// attacker_weapon_1,
|
||||
// attacker_weapon_1_mods,
|
||||
// attacker_weapon_2,
|
||||
// attacker_weapon_2_mods,
|
||||
// attacker_weapon_3,
|
||||
// attacker_weapon_3_mods,
|
||||
// attacker_offhand_weapon_1,
|
||||
// attacker_offhand_weapon_2,
|
||||
// victim_name,
|
||||
// victim_id,
|
||||
// victim_current_weapon,
|
||||
// victim_current_weapon_mods,
|
||||
// victim_weapon_1,
|
||||
// victim_weapon_1_mods,
|
||||
// victim_weapon_2,
|
||||
// victim_weapon_2_mods,
|
||||
// victim_weapon_3,
|
||||
// victim_weapon_3_mods,
|
||||
// victim_offhand_weapon_1,
|
||||
// victim_offhand_weapon_2,
|
||||
// cause_of_death,
|
||||
// distance
|
||||
// })
|
||||
// console.error(e)
|
||||
// })
|
||||
})()
|
||||
}
|
||||
)
|
||||
export default router
|
||||
@@ -0,0 +1,54 @@
|
||||
import { RequestHandler, Request } from "express";
|
||||
import typia from "typia";
|
||||
|
||||
type WeaponKillData = {
|
||||
name: string;
|
||||
mods: number;
|
||||
};
|
||||
|
||||
type PlayerKillData = {
|
||||
velocity: number;
|
||||
name: string;
|
||||
loadout: {
|
||||
ordnance: WeaponKillData;
|
||||
secondary: WeaponKillData;
|
||||
primary: WeaponKillData;
|
||||
tactical: WeaponKillData;
|
||||
anti_titan: WeaponKillData;
|
||||
};
|
||||
current_weapon: WeaponKillData;
|
||||
state: string;
|
||||
titan: string | null;
|
||||
id: number | bigint;
|
||||
cloaked: boolean;
|
||||
};
|
||||
|
||||
export type MatchData = {
|
||||
server_name: string;
|
||||
game_map: string;
|
||||
gamemode: string;
|
||||
air_accel: boolean;
|
||||
};
|
||||
|
||||
export type KillData = {
|
||||
game_time: number;
|
||||
player_count: number;
|
||||
match_id: number;
|
||||
victim: PlayerKillData;
|
||||
attacker: PlayerKillData;
|
||||
distance: number;
|
||||
cause_of_death: string;
|
||||
};
|
||||
|
||||
type RequestBody<T> = Request<{}, {}, T>;
|
||||
|
||||
export const validateBody =
|
||||
<T>(checker: (input: T) => typia.IValidation<T>): RequestHandler =>
|
||||
(req, res, next) => {
|
||||
const result: typia.IValidation<T> = checker(req.body);
|
||||
if (!result.success) {
|
||||
res.status(400).send({ errors: result.errors });
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
+5
-1
@@ -1,8 +1,12 @@
|
||||
// Typed routes from https://urosstok.com/blog/typed-routes-in-express
|
||||
|
||||
|
||||
|
||||
import * as dotenv from 'dotenv'
|
||||
import express from 'express'
|
||||
import cors from 'cors'
|
||||
import { dbReady } from './db/db'
|
||||
import server from './server/server'
|
||||
import server from './generated/server'
|
||||
dotenv.config()
|
||||
|
||||
const app = express()
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { Router, Request, Response } from "express";
|
||||
import { header } from "express-validator";
|
||||
import db, { /* CreateKillRecord, */ CheckServerToken } from "../db/db";
|
||||
import { validateErrors } from "../common";
|
||||
import { KillData, MatchData, validateBody } from "../server/types";
|
||||
import typia from "typia";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// auth middleware
|
||||
router.post(
|
||||
"/*",
|
||||
header("authorization")
|
||||
.exists({ checkFalsy: true })
|
||||
.withMessage("Missing Authorization Header")
|
||||
.bail()
|
||||
.custom((e) => e.split(" ")[0].toLowerCase() === "bearer")
|
||||
.withMessage("Authorization Token is not Bearer"),
|
||||
validateErrors,
|
||||
(req, res, next) => {
|
||||
void (async () => {
|
||||
if (!req.headers.authorization) {
|
||||
return res.sendStatus(401);
|
||||
}
|
||||
const query = await CheckServerToken(
|
||||
req.headers.authorization.split(" ")[1]
|
||||
);
|
||||
if (!query?.host_id) {
|
||||
console.error(
|
||||
`incorrect token : ${
|
||||
req.headers.authorization.split(" ")[1]
|
||||
} with IP ${
|
||||
req.headers["x-forwarded-for"]?.toString() ??
|
||||
req.socket.remoteAddress?.toString() ??
|
||||
""
|
||||
}`
|
||||
);
|
||||
return res.status(401).send({
|
||||
errors: [
|
||||
{
|
||||
msg: "Incorrect Token",
|
||||
param: "authorization",
|
||||
location: "headers",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
res.locals.host_id = query.host_id;
|
||||
next();
|
||||
})();
|
||||
}
|
||||
);
|
||||
|
||||
// Route to check auth
|
||||
router.post("/", (req, res) => {
|
||||
res.sendStatus(200);
|
||||
});
|
||||
|
||||
// const serversCount: Record<string, number> = {}
|
||||
// const serversTimeout: Record<string, NodeJS.Timeout> = {}
|
||||
|
||||
// same rate limiting code as register. max 10 kills per server every 1 sec. should be enough.
|
||||
/* router.post('/kill', (req, res, next) => {
|
||||
let host = Number(req.query.serverId)
|
||||
if (serversCount[serverId] > 2) {
|
||||
return res.status(429).json({
|
||||
error: 'too many requests. Are players really making that much kills ?'
|
||||
})
|
||||
}
|
||||
clearTimeout(serversTimeout[serverId])
|
||||
serversCount[serverId] =
|
||||
serversCount[serverId] ?? (serversCount[serverId] + 1) | 1
|
||||
serversTimeout[serverId] = setTimeout(() => {
|
||||
serversCount[serverId] = 0
|
||||
}, 1000)
|
||||
next()
|
||||
}) */
|
||||
|
||||
type RequestBody<T> = Request<{}, {}, T>;
|
||||
|
||||
router.post(
|
||||
"/kill",
|
||||
validateBody(typia.createValidate<KillData>()),
|
||||
(req: RequestBody<KillData>, res: Response) => {
|
||||
void (async () => {
|
||||
const host_id = res.locals.host_id;
|
||||
const { match_id } = req.body;
|
||||
const match = await db
|
||||
.selectFrom("ToneAPI_v3.match")
|
||||
.select(["ToneAPI_v3.match.host_id", "ToneAPI_v3.match.match_id"])
|
||||
.where("ToneAPI_v3.match.host_id", "=", host_id)
|
||||
.where("ToneAPI_v3.match.match_id", "=", match_id)
|
||||
.executeTakeFirst();
|
||||
if (!match) {
|
||||
res
|
||||
.status(403)
|
||||
.send({
|
||||
errors: [
|
||||
{
|
||||
msg: "Match does not exists",
|
||||
param: "match_id",
|
||||
location: "body",
|
||||
},
|
||||
],
|
||||
});
|
||||
return;
|
||||
}
|
||||
req.body;
|
||||
})();
|
||||
}
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/match",
|
||||
validateBody(typia.createValidate<MatchData>()),
|
||||
(req: RequestBody<MatchData>, res: Response) => {
|
||||
void (async () => {
|
||||
const host_id = res.locals.host_id;
|
||||
const { air_accel, server_name, game_map, gamemode } = req.body;
|
||||
const server = await db
|
||||
.selectFrom("ToneAPI_v3.server")
|
||||
.selectAll()
|
||||
.where("ToneAPI_v3.server.host_id", "=", host_id)
|
||||
.where("ToneAPI_v3.server.server_name", "=", server_name)
|
||||
.executeTakeFirst();
|
||||
if (!server) {
|
||||
await db
|
||||
.insertInto("ToneAPI_v3.server")
|
||||
.values({ host_id, server_name })
|
||||
.execute();
|
||||
}
|
||||
const match = await db
|
||||
.insertInto("ToneAPI_v3.match")
|
||||
.values({ air_accel, server_name, game_map, gamemode, host_id })
|
||||
.executeTakeFirst();
|
||||
res.status(201).send({ match: match.insertId });
|
||||
})();
|
||||
}
|
||||
);
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user