Big refactor, split client and server. add weapon get route
This commit is contained in:
+4
-1
@@ -1,4 +1,7 @@
|
||||
POSTGRES_HOST="localhost"
|
||||
POSTGRES_DATABASE="postgres"
|
||||
POSTGRES_USER="postgres"
|
||||
POSTGRES_PASSWORD="postgres"
|
||||
POSTGRES_PASSWORD="postgres"
|
||||
REDIS_USER="default"
|
||||
REDIS_PASSWORD=""
|
||||
REDIS_URL="redis://awesome.redis.server:6379"
|
||||
Vendored
+25
-20
@@ -1,21 +1,26 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"program": "${workspaceFolder}/src/index.ts",
|
||||
"preLaunchTask": "tsc: build - tsconfig.json",
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Server API",
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"program": "${workspaceFolder}/src/server.ts",
|
||||
"preLaunchTask": "tsc: build - tsconfig.json",
|
||||
"outFiles": ["${workspaceFolder}/out/**/*.js"]
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Client API",
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"program": "${workspaceFolder}/src/client.ts",
|
||||
"preLaunchTask": "tsc: build - tsconfig.json",
|
||||
"outFiles": ["${workspaceFolder}/out/**/*.js"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Generated
+84
-2
@@ -12,6 +12,7 @@
|
||||
"express-validator": "^6.15.0",
|
||||
"kysely": "^0.23.4",
|
||||
"pg": "^8.9.0",
|
||||
"redis": "^4.6.5",
|
||||
"ts-node": "^10.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -2553,6 +2554,59 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/bloom": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
|
||||
"integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/client": {
|
||||
"version": "1.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.6.tgz",
|
||||
"integrity": "sha512-dFD1S6je+A47Lj22jN/upVU2fj4huR7S9APd7/ziUXsIXDL+11GPYti4Suv5y8FuXaN+0ZG4JF+y1houEJ7ToA==",
|
||||
"dependencies": {
|
||||
"cluster-key-slot": "1.1.2",
|
||||
"generic-pool": "3.9.0",
|
||||
"yallist": "4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/graph": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz",
|
||||
"integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/json": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz",
|
||||
"integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/search": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.2.tgz",
|
||||
"integrity": "sha512-/cMfstG/fOh/SsE+4/BQGeuH/JJloeWuH+qJzM8dbxuWvdWibWAOAHHCZTMPhV3xIlH4/cUEIA8OV5QnYpaVoA==",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/time-series": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz",
|
||||
"integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==",
|
||||
"peerDependencies": {
|
||||
"@redis/client": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sinclair/typebox": {
|
||||
"version": "0.25.24",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz",
|
||||
@@ -3705,6 +3759,14 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/cluster-key-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
|
||||
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@@ -4888,6 +4950,14 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/generic-pool": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
|
||||
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
@@ -7151,6 +7221,19 @@
|
||||
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/redis": {
|
||||
"version": "4.6.5",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-4.6.5.tgz",
|
||||
"integrity": "sha512-O0OWA36gDQbswOdUuAhRL6mTZpHFN525HlgZgDaVNgCJIAZR3ya06NTESb0R+TUZ+BFaDpz6NnnVvoMx9meUFg==",
|
||||
"dependencies": {
|
||||
"@redis/bloom": "1.2.0",
|
||||
"@redis/client": "1.5.6",
|
||||
"@redis/graph": "1.1.0",
|
||||
"@redis/json": "1.0.4",
|
||||
"@redis/search": "1.1.2",
|
||||
"@redis/time-series": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/regenerate": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||
@@ -8185,8 +8268,7 @@
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.1",
|
||||
|
||||
+4
-2
@@ -7,6 +7,7 @@
|
||||
"express-validator": "^6.15.0",
|
||||
"kysely": "^0.23.4",
|
||||
"pg": "^8.9.0",
|
||||
"redis": "^4.6.5",
|
||||
"ts-node": "^10.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -29,7 +30,8 @@
|
||||
"main": "out/index.js",
|
||||
"scripts": {
|
||||
"build": "npx tsc",
|
||||
"start": "node out/index.js",
|
||||
"startServer": "node out/server.js",
|
||||
"startClient": "node out/client.js",
|
||||
"test": "jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
import { createClient } from 'redis'
|
||||
|
||||
const client = createClient({
|
||||
url: process.env.REDIS_URL,
|
||||
username: process.env.REDIS_USER,
|
||||
password: process.env.REDIS_PASSWORD
|
||||
})
|
||||
|
||||
client.on('error', (err) => console.log('Redis Client Error', err))
|
||||
|
||||
export function cacheReady() {
|
||||
return client.connect()
|
||||
}
|
||||
export default client
|
||||
|
||||
//await client.set('key', 'value');
|
||||
//const value = await client.get('key');
|
||||
//await client.disconnect();
|
||||
+24
-18
@@ -1,21 +1,27 @@
|
||||
import { Router } from 'express'
|
||||
const router = Router()
|
||||
//timeout middleware ?
|
||||
router.get('/*', (req, res, next) => {
|
||||
next()
|
||||
import * as dotenv from 'dotenv'
|
||||
dotenv.config()
|
||||
import express from 'express'
|
||||
import cors from 'cors'
|
||||
import client from './client/client'
|
||||
import { dbReady } from './db/db'
|
||||
import { cacheReady } from './cache/redis'
|
||||
|
||||
const app = express()
|
||||
const port = 3001
|
||||
|
||||
app.use(express.json())
|
||||
|
||||
app.use(cors())
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Hello World!')
|
||||
})
|
||||
|
||||
router.get('/weapons/', (req, res, next) => {})
|
||||
app.use('/v1/client', client)
|
||||
|
||||
router.get('/maps/', (req, res, next) => {})
|
||||
|
||||
router.get('/players', (req, res, next) => {})
|
||||
router.get('/players/:playerId', (req, res, next) => {})
|
||||
|
||||
router.get('/servers/', (req, res, next) => {})
|
||||
router.get('/servers/:serverId/', (req, res, next) => {})
|
||||
router.get('/servers/:serverId/players', (req, res, next) => {})
|
||||
router.get('/servers/:serverId/players/:playerId', (req, res, next) => {})
|
||||
|
||||
router.get('/servers/:serverId/weapons', (req, res, next) => {})
|
||||
export default router
|
||||
dbReady().then((e) => {
|
||||
cacheReady().then((e) => {
|
||||
app.listen(port, '0.0.0.0', () => {
|
||||
console.log(`Example app listening on port ${port}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Router } from 'express'
|
||||
import weapons from './weapons'
|
||||
const router = Router()
|
||||
//timeout middleware ?
|
||||
router.get('/*', (req, res, next) => {
|
||||
next()
|
||||
})
|
||||
|
||||
router.get('/weapons/', (req, res, next) => {})
|
||||
|
||||
router.get('/maps/', (req, res, next) => {})
|
||||
|
||||
router.get('/players', (req, res, next) => {})
|
||||
router.get('/players/:playerId', (req, res, next) => {})
|
||||
|
||||
router.get('/servers/', (req, res, next) => {})
|
||||
router.get('/servers/:serverId/', (req, res, next) => {})
|
||||
router.get('/servers/:serverId/players', (req, res, next) => {})
|
||||
router.get('/servers/:serverId/players/:playerId', (req, res, next) => {})
|
||||
|
||||
router.use('/', weapons)
|
||||
export default router
|
||||
@@ -0,0 +1,63 @@
|
||||
import { Router } from 'express'
|
||||
import { param, validationResult } from 'express-validator'
|
||||
import cache from '../cache/redis'
|
||||
import db from '../db/db'
|
||||
const router = Router()
|
||||
const { count, max } = db.fn
|
||||
|
||||
router.get(
|
||||
'/servers/:serverId/weapons',
|
||||
param('serverId').exists().toInt().isInt(),
|
||||
async (req, res) => {
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
console.error(JSON.stringify(errors))
|
||||
return res.status(400).json({ errors: errors.array() })
|
||||
}
|
||||
if (!req.params) {
|
||||
res.sendStatus(500)
|
||||
return
|
||||
}
|
||||
const server = Number(req.params.serverId)
|
||||
await processTopWeapons(server)
|
||||
const data = await cache.HGETALL(`servers:${server}:weapons`)
|
||||
delete data.last_entry
|
||||
res.status(200).send(data)
|
||||
}
|
||||
)
|
||||
|
||||
async function processTopWeapons(server: number) {
|
||||
let last_entry =
|
||||
Number(await cache.HGET(`servers:${server}:weapons`, 'last_entry')) || 0
|
||||
const newData = await db
|
||||
.selectFrom(['kill'])
|
||||
.select([
|
||||
count<number>('kill.id').as('num_kills'),
|
||||
'kill.cause_of_death',
|
||||
max('kill.id').as('last_entry')
|
||||
])
|
||||
.where('server', '=', server)
|
||||
.where('kill.id', '>', last_entry)
|
||||
.groupBy('cause_of_death')
|
||||
.execute()
|
||||
//if no new kills
|
||||
if (newData.length == 0) {
|
||||
return
|
||||
}
|
||||
last_entry = newData.reduce(
|
||||
(acc, current) => (current.last_entry > acc ? current.last_entry : acc),
|
||||
0
|
||||
)
|
||||
const promises: Promise<number>[] = []
|
||||
newData.forEach(({ num_kills, cause_of_death }) => {
|
||||
promises.push(
|
||||
cache.HINCRBY(`servers:${server}:weapons`, cause_of_death, num_kills)
|
||||
)
|
||||
})
|
||||
promises.push(
|
||||
cache.HSET(`servers:${server}:weapons`, 'last_entry', newData[0].last_entry)
|
||||
)
|
||||
await Promise.all(promises)
|
||||
}
|
||||
|
||||
export default router
|
||||
+7
-1
@@ -12,7 +12,13 @@ const db = new Kysely<Database>({
|
||||
user: process.env.POSTGRES_USER,
|
||||
password: process.env.POSTGRES_PASSWORD
|
||||
})
|
||||
})
|
||||
}),
|
||||
log(event) {
|
||||
if (event.level === 'query') {
|
||||
console.log(event.query.sql)
|
||||
console.log(event.query.parameters)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
interface RemoveFromKill {
|
||||
|
||||
@@ -2,7 +2,6 @@ import * as dotenv from 'dotenv'
|
||||
dotenv.config()
|
||||
import express from 'express'
|
||||
import cors from 'cors'
|
||||
import client from './client'
|
||||
import { dbReady } from './db/db'
|
||||
import server from './server/server'
|
||||
|
||||
@@ -16,8 +15,7 @@ app.get('/', (req, res) => {
|
||||
res.send('Hello World!')
|
||||
})
|
||||
|
||||
app.use('/v1/', server)
|
||||
app.use('/v1/', client)
|
||||
app.use('/v1/servers', server)
|
||||
|
||||
dbReady().then((e) => {
|
||||
app.listen(port, '0.0.0.0', () => {
|
||||
@@ -12,7 +12,7 @@ const hostsCount: { [id: string]: number } = {}
|
||||
const hostsTimeout: { [id: string]: NodeJS.Timeout } = {}
|
||||
|
||||
//Very simple rate limiting. max 2 registers per IP every 5 mins. Maybe 2 is a bit few ?
|
||||
router.post('/servers/register', (req, res, next) => {
|
||||
router.post('/register', (req, res, next) => {
|
||||
let ip =
|
||||
req.header('x-forwarded-for') || req.socket.remoteAddress || 'undefined'
|
||||
if (hostsCount[ip] > 2) {
|
||||
@@ -31,7 +31,7 @@ router.post('/servers/register', (req, res, next) => {
|
||||
})
|
||||
|
||||
router.post(
|
||||
'/servers/register',
|
||||
'/register',
|
||||
body(['name', 'description']).isString().withMessage('must be strings'),
|
||||
body('auth_port')
|
||||
.toInt()
|
||||
|
||||
@@ -10,7 +10,7 @@ router.use('/', register)
|
||||
|
||||
//auth middleware
|
||||
router.post(
|
||||
'/servers/:serverId/*',
|
||||
'/:serverId/*',
|
||||
header('authorization')
|
||||
.exists({ checkFalsy: true })
|
||||
.withMessage('Missing Authorization Header')
|
||||
@@ -37,7 +37,7 @@ router.post(
|
||||
)
|
||||
|
||||
//Route to check auth
|
||||
router.post('/servers/:serverId', (req, res) => {
|
||||
router.post('/:serverId', (req, res) => {
|
||||
res.sendStatus(200)
|
||||
})
|
||||
|
||||
@@ -45,7 +45,7 @@ const serversCount: { [id: string]: number } = {}
|
||||
const serversTimeout: { [id: string]: NodeJS.Timeout } = {}
|
||||
|
||||
//same rate limiting code as register. max 10 kills per server every 1 sec. should be enough.
|
||||
router.post('/servers/:serverId/kill', (req, res, next) => {
|
||||
router.post('/:serverId/kill', (req, res, next) => {
|
||||
let serverId = req.body.serverId || 'undefined'
|
||||
if (serversCount[serverId] > 2) {
|
||||
return res.status(429).json({
|
||||
@@ -62,7 +62,7 @@ router.post('/servers/:serverId/kill', (req, res, next) => {
|
||||
})
|
||||
|
||||
router.post(
|
||||
'/servers/:serverId/kill',
|
||||
'/:serverId/kill',
|
||||
body([
|
||||
'attacker_current_weapon_mods',
|
||||
'attacker_weapon_1_mods',
|
||||
|
||||
Reference in New Issue
Block a user