Compare commits

..
10 Commits
Author SHA1 Message Date
peter bbe080ab7f update character screen 2021-03-14 18:49:13 +01:00
peter 75c48273ff autorefresh on reserve update 2021-01-31 18:36:36 +01:00
peter c2d2917de3 changereserve buttons 2021-01-31 18:19:33 +01:00
peter 78abe78903 adminSetAbsent 2020-12-10 11:31:54 +01:00
peter d4a528e61e t3 tokens 2020-12-02 17:46:22 +01:00
peter c09c9ab67c 3 days bench bonus, hopefully better bans, naxx items 2020-11-29 17:21:14 +01:00
peter f9008e67bd HTTP request logging format 2 2020-10-02 20:22:20 +02:00
peter 7e2c1d5761 HTTP request logging format 2020-10-02 18:42:46 +02:00
peter 1765c2eabc access log 2020-10-02 14:44:39 +02:00
peter eb58ffc013 reduce log spam 2020-09-15 20:29:52 +02:00
23 changed files with 624 additions and 117 deletions
+1
View File
@@ -17,6 +17,7 @@ log.txt
!src/**/* !src/**/*
!test/**/*
!migrations/**/* !migrations/**/*
node_modules node_modules
src/frontend/node_modules src/frontend/node_modules
+70 -3
View File
@@ -93,12 +93,29 @@
} }
} }
}, },
"@types/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
"requires": {
"@types/connect": "*",
"@types/node": "*"
}
},
"@types/color-name": { "@types/color-name": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"dev": true "dev": true
}, },
"@types/connect": {
"version": "3.4.33",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
"integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
"requires": {
"@types/node": "*"
}
},
"@types/engine.io": { "@types/engine.io": {
"version": "3.1.4", "version": "3.1.4",
"resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.4.tgz", "resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.4.tgz",
@@ -107,6 +124,27 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/express": {
"version": "4.17.8",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.8.tgz",
"integrity": "sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ==",
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"@types/express-serve-static-core": {
"version": "4.17.13",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz",
"integrity": "sha512-RgDi5a4nuzam073lRGKTUIaL3eF2+H7LJvJ8eUnCI0wA6SNjXc44DCmWNiTLs/AZ7QlsFWZiw/gTG3nSQGL0fA==",
"requires": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*"
}
},
"@types/fs-extra": { "@types/fs-extra": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.0.0.tgz",
@@ -133,6 +171,11 @@
"@types/lodash": "*" "@types/lodash": "*"
} }
}, },
"@types/mime": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz",
"integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q=="
},
"@types/mocha": { "@types/mocha": {
"version": "5.2.7", "version": "5.2.7",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
@@ -151,6 +194,25 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/qs": {
"version": "6.9.5",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz",
"integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ=="
},
"@types/range-parser": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
},
"@types/serve-static": {
"version": "1.13.5",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.5.tgz",
"integrity": "sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ==",
"requires": {
"@types/express-serve-static-core": "*",
"@types/mime": "*"
}
},
"@types/socket.io": { "@types/socket.io": {
"version": "2.1.10", "version": "2.1.10",
"resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.10.tgz", "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.10.tgz",
@@ -4227,6 +4289,11 @@
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
}, },
"rate-limiter-flexible": {
"version": "2.1.10",
"resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-2.1.10.tgz",
"integrity": "sha512-Pa+8TPD4xYaiCUB5K4a/+j2FHDUe4HP1g49JmKEmkOkhqPaeVqxJsZuuVaza/svSCOT+V73vtsyBiSFK/e1yXw=="
},
"raw-body": { "raw-body": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
@@ -4410,9 +4477,9 @@
} }
}, },
"rpclibrary": { "rpclibrary": {
"version": "2.0.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-2.0.1.tgz", "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-2.3.1.tgz",
"integrity": "sha512-phdU0TkrnNVXdFa5PKkbWvn+MxocowvqalEUG1jfpkc1CoQkJ9AXa4OXSEu5r2a2IFhVauYva/qXv1em5yrHSg==", "integrity": "sha512-y6F6gYxm9joJcnyxNeEoUSAbYGsaogcEKb1bxVB97+HcT4J6q9yw2fGnWS1FeeAUbTLUwASdlqJvRLPLiMPlcQ==",
"requires": { "requires": {
"@types/socket.io": "^2.1.8", "@types/socket.io": "^2.1.8",
"@types/socket.io-client": "^1.4.33", "@types/socket.io-client": "^1.4.33",
+3 -1
View File
@@ -25,6 +25,7 @@
"author": "frontblock.me", "author": "frontblock.me",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@types/express": "^4.17.8",
"@types/mocha": "^5.2.7", "@types/mocha": "^5.2.7",
"@types/node": "^14.0.27", "@types/node": "^14.0.27",
"bsert": "0.0.10", "bsert": "0.0.10",
@@ -46,9 +47,10 @@
"ngx-cookie-service": "^2.4.0", "ngx-cookie-service": "^2.4.0",
"node-fetch": "^2.6.0", "node-fetch": "^2.6.0",
"path": "^0.12.7", "path": "^0.12.7",
"rate-limiter-flexible": "^2.1.10",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.0", "rimraf": "^3.0.0",
"rpclibrary": "^2.0.1", "rpclibrary": "^2.3.1",
"simple-git": "^1.124.0", "simple-git": "^1.124.0",
"spawn-sync": "^2.0.0", "spawn-sync": "^2.0.0",
"sqlite3": "^4.2.0", "sqlite3": "^4.2.0",
+37 -18
View File
@@ -15,12 +15,14 @@ import { CharacterManager } from '../Components/Character/CharacterManager';
import { UserManager } from '../Components/User/UserManager'; import { UserManager } from '../Components/User/UserManager';
import { RootComponent } from '../Injector/ServiceDecorator'; import { RootComponent } from '../Injector/ServiceDecorator';
import { TableDefinitionExporter } from '../Types/Interfaces'; import { TableDefinitionExporter } from '../Types/Interfaces';
import { AdminConf, TableDefiniton } from '../Types/Types'; import { AdminConf, TableDefiniton, SomeOf } from '../Types/Types';
import { RPCConfigLoader } from '../Components/RPCConfigLoader'; import { RPCConfigLoader } from '../Components/RPCConfigLoader';
import { FrontworkComponent } from '../Types/FrontworkComponent'; import { FrontworkComponent } from '../Types/FrontworkComponent';
import { IAdmin } from './Interface'; import { IAdmin } from './Interface';
import { Injector } from '../Injector/Injector'; import { Injector } from '../Injector/Injector';
import { PubSub } from '../Components/PubSub/PubSub'; import { PubSub } from '../Components/PubSub/PubSub';
import { BurstyRateLimiter, RateLimiterMemory, RateLimiterAbstract } from 'rate-limiter-flexible'
@RootComponent({ @RootComponent({
injectable: IAdmin, injectable: IAdmin,
@@ -39,7 +41,7 @@ export class FrontworkAdmin
config: RPCConfigLoader<AdminConf> config: RPCConfigLoader<AdminConf>
rpcServer: RPCServer rpcServer: RPCServer
private express private express : express.Application
private httpServer private httpServer
constructor(private frontworkComponents: FrontworkComponent[] = []) { constructor(private frontworkComponents: FrontworkComponent[] = []) {
@@ -66,14 +68,26 @@ export class FrontworkAdmin
} }
async start() { async start() {
const ONE_REQUEST = 15
let port: number = this.config.getConfig().httpPort let port: number = this.config.getConfig().httpPort
const rateLimiter = new BurstyRateLimiter(
new RateLimiterMemory({
points: 2*ONE_REQUEST,
duration: 1,
}),
new RateLimiterMemory({
keyPrefix: 'burst',
points: 5*ONE_REQUEST,
duration: 60,
})
)
await this.makeKnex() await this.makeKnex()
const app = await this.makeExpress() const app = await this.makeExpress(rateLimiter)
const httpServer = new http.Server(app) const httpServer = new http.Server(app)
await this.startWebsocket(httpServer) await this.startWebsocket(httpServer)
httpServer.listen(port) httpServer.listen(port)
await this.attachAngularSSR(app) await this.attachAngularSSR(app, rateLimiter)
getLogger('Admin#startWebsocket').debug("Webserver up on", port) getLogger('Admin#startWebsocket').debug("Webserver up on", port)
await Promise.all(this.frontworkComponents.map(c => c.initialize ? c.initialize() : undefined)) await Promise.all(this.frontworkComponents.map(c => c.initialize ? c.initialize() : undefined))
@@ -125,48 +139,53 @@ export class FrontworkAdmin
} }
], { ], {
errorHandler: (sock, err, rpc, args) => { errorHandler: (sock, err, rpc, args) => {
console.log(rpc, err); getLogger("startWebsocket#errorHandler").error("RPC", rpc)
getLogger("startWebsocket#errorHandler").error("ERR", err)
getLogger("startWebsocket#errorHandler").error("ARGS", args)
} }
}) })
.attach(httpServer) .attach(httpServer)
} }
private async makeExpress() { private async makeExpress(rateLimiter : SomeOf<RateLimiterAbstract> = new RateLimiterMemory({ points: 6, duration: 3 })) {
if (this.httpServer != null || this.express != null) { if (this.httpServer != null || this.express != null) {
getLogger('Admin#startWebserver').warn("Webserver is already running") getLogger('Admin#startWebserver').warn("Webserver is already running")
return return
} }
this.express = express() this.express = express()
const distFolder = "../../../../dist" const distFolder = "../../../../dist"
this.express.get('*.*', (req, res) => { this.express.get('*.*', (req, res) => {
//console.log('*.*', req.path); rateLimiter.consume(req.ip)
try{ .then(_ => {
const filepath = Path.join(__dirname, distFolder, 'browser', decodeURIComponent(req.path)) const filepath = Path.join(__dirname, distFolder, 'browser', decodeURIComponent(req.path))
if(!existsSync(filepath)){ if(!existsSync(filepath)){
throw new Error("Bad file access: "+filepath) getLogger('Admin#startWebserver#serveFile').error(String(new Error("404 BAD REQUEST "+filepath+ " FROM "+req.ip)))
res.send("404 Not found")
res.status(404)
return
} }
res.sendFile(filepath) res.sendFile(filepath)
res.status(200) res.status(200)
}catch(e){
getLogger('Admin#startWebserver#serveFile').error(String(e))
res.send("404 NOT FOUND")
res.status(404)
}
}) })
.catch(_ => {
getLogger('Admin#startWebserver#serveFile').error(String(new Error("429 BAD REQUEST "+req.path+" FROM "+req.ip)))
res.send('429 Too many requests')
res.status(429)
})
})
return this.express return this.express
} }
attachAngularSSR = async (express : express.Application) => { attachAngularSSR = async (express : express.Application, rateLimiter : SomeOf<RateLimiterAbstract> = new RateLimiterMemory({ points: 6, duration: 3 })) => {
const distFolder = "../../../../dist" const distFolder = "../../../../dist"
const ngExpressServer = Path.join(__dirname, distFolder, 'server.js') const ngExpressServer = Path.join(__dirname, distFolder, 'server.js')
let port: number = this.config.getConfig().httpPort let port: number = this.config.getConfig().httpPort
try { try {
const req = require(distFolder + "/server.js") const req = require(distFolder + "/server.js")
await req.attachExpress(express, './dist', getLogger('angularSSR#')) await req.attachExpress(express, './dist', getLogger('angularSSR#'), rateLimiter)
getLogger('Admin#startWebserver').debug('Frontend from ' + ngExpressServer + " loaded") getLogger('Admin#startWebserver').debug('Frontend from ' + ngExpressServer + " loaded")
} catch (e) { } catch (e) {
getLogger('Admin#startWebserver').error(e) getLogger('Admin#startWebserver').error(e)
@@ -30,7 +30,13 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
this.getHeadCount this.getHeadCount
] ]
RPCFeatures = () => [] RPCFeatures = () => [{
name: 'manageCharacter' as 'manageCharacter',
RPCs: [
this.updateCharacter
]
}]
getTableDefinitions = (): TableDefiniton[] => [ getTableDefinitions = (): TableDefiniton[] => [
{ {
@@ -66,6 +72,22 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
}) })
} }
updateCharacter = async (character: Character) : Promise<Character> => {
await this.admin
.knex('characters')
.update(<Character>{
race: character.race,
specid: character.specid,
charactername: character.charactername.toLowerCase(),
userid: character.userid
})
.where({
id: character.id
})
return await this.getCharacterByName(character.charactername) as Character
}
createCharacter = async (userToken: string, character : Character) : Promise<Character> => { createCharacter = async (userToken: string, character : Character) : Promise<Character> => {
try{ try{
character.charactername = character.charactername.toLowerCase() character.charactername = character.charactername.toLowerCase()
@@ -10,4 +10,6 @@ export class ICharacterManager{
getCharactersOfUser: (username: string) => Promise<(Character & Spec)[]> getCharactersOfUser: (username: string) => Promise<(Character & Spec)[]>
getUserOfCharacter: (character:Character) => Promise<User> getUserOfCharacter: (character:Character) => Promise<User>
getHeadCount: (clazz: Class) => Promise<number> getHeadCount: (clazz: Class) => Promise<number>
updateCharacter: (character: Character) => Promise<Character>
} }
@@ -14,5 +14,7 @@ export type CharacterManagerIfc = {
} }
export type CharacterManagerFeatureIfc = { export type CharacterManagerFeatureIfc = {
manageCharacter: {
updateCharacter: ICharacterManager['updateCharacter']
}
} }
+3
View File
@@ -15,4 +15,7 @@ export class IItemManager{
wipeCurrencyAndItems: () => Promise<void> wipeCurrencyAndItems: () => Promise<void>
decayTokens: (tier: Tiers, amount?:number) => Promise<void> decayTokens: (tier: Tiers, amount?:number) => Promise<void>
decayTokensOfCharacter: (tier: Tiers, character:Character, amount?:number) => Promise<void> decayTokensOfCharacter: (tier: Tiers, character:Character, amount?:number) => Promise<void>
increaseReserve: (token: Character & SRToken & Item, raidid?:number) => Promise<void>
decreaseReserve: (token: Character & SRToken & Item, raidid?:number) => Promise<void>
} }
+38 -3
View File
@@ -62,6 +62,12 @@ export class ItemManager
RPCs: () => [ RPCs: () => [
this.decayTokensOfCharacter this.decayTokensOfCharacter
] ]
}, {
name: 'changeReserve' as 'changeReserve',
RPCs: () => [
this.decreaseReserve,
this.increaseReserve
]
}] }]
notifyRaid = async (raid: Raid | { id: number }) => { notifyRaid = async (raid: Raid | { id: number }) => {
@@ -253,7 +259,7 @@ export class ItemManager
itemname: item.itemname itemname: item.itemname
}) })
} else { } else {
getLogger('ItemManager').debug('new reserve') //getLogger('ItemManager').debug('new reserve')
await this.admin await this.admin
.knex(item.tier + 'tokens') .knex(item.tier + 'tokens')
.transacting(tx) .transacting(tx)
@@ -374,7 +380,7 @@ export class ItemManager
} }
decayTokens = async (tier: Tiers, amount = 1) => { decayTokens = async (tier: Tiers, amount = 1) => {
getLogger('ItemManager#decayTokens').debug(tier) //getLogger('ItemManager#decayTokens').debug(tier)
await this.admin await this.admin
.knex(tier + 'tokens') .knex(tier + 'tokens')
@@ -387,7 +393,7 @@ export class ItemManager
} }
decayTokensOfCharacter = async (tier: Tiers, character:Character) => { decayTokensOfCharacter = async (tier: Tiers, character:Character) => {
getLogger('ItemManager#decayTokensOfCharacter').debug(character, tier) //getLogger('ItemManager#decayTokensOfCharacter').debug(character, tier)
await this.admin await this.admin
.knex(tier + 'tokens') .knex(tier + 'tokens')
.where({ .where({
@@ -427,6 +433,35 @@ export class ItemManager
) )
} }
} }
increaseReserve = async (token: Character & SRToken & Item, raidid?:number) : Promise<void> => {
await this.admin
.knex(token.tier+"tokens")
.where({
characterid: token.characterid,
itemname: token.itemname
}).update({
level: token.level+1
})
if(raidid)
this.raidManager.notifyRaid({id: raidid})
}
decreaseReserve = async (token: Character & SRToken & Item, raidid?:number) : Promise<void> => {
await this.admin
.knex(token.tier+"tokens")
.where({
characterid: token.characterid,
itemname: token.itemname
}).update({
level: Math.max(1, token.level-1)
})
if(raidid)
this.raidManager.notifyRaid({id: raidid})
}
} }
function toStats(obj:any){ function toStats(obj:any){
@@ -23,4 +23,8 @@ export type ItemManagerFeatureIfc = {
decay: { decay: {
decayTokensOfCharacter: IItemManager['decayTokensOfCharacter'] decayTokensOfCharacter: IItemManager['decayTokensOfCharacter']
} }
changeReserve: {
increaseReserve: IItemManager['increaseReserve']
decreaseReserve: IItemManager['decreaseReserve']
}
} }
+1
View File
@@ -16,4 +16,5 @@ export class IRaidManager{
startRaid: (raid:Raid) => Promise<RaidData> startRaid: (raid:Raid) => Promise<RaidData>
adminUnsign: (character:Character, raid:Raid) => Promise<any> adminUnsign: (character:Character, raid:Raid) => Promise<any>
cancelRaid: (raid: Raid) => Promise<void> cancelRaid: (raid: Raid) => Promise<void>
notifyRaid : (raid: Raid | { id: number }) => Promise<void>
} }
+17 -2
View File
@@ -13,6 +13,8 @@ import { ItemManager } from "../Item/ItemManager";
import { IPubSub } from "../PubSub/Interface"; import { IPubSub } from "../PubSub/Interface";
import { getLogger } from "log4js"; import { getLogger } from "log4js";
@Injectable(IRaidManager) @Injectable(IRaidManager)
export class RaidManager export class RaidManager
implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManager { implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManager {
@@ -27,7 +29,7 @@ export class RaidManager
@Inject(ICharacterManager) @Inject(ICharacterManager)
private characterManager: ICharacterManager private characterManager: ICharacterManager
@Inject(ItemManager) @Inject(IItemManager)
private itemManager: IItemManager private itemManager: IItemManager
@Inject(IPubSub) @Inject(IPubSub)
@@ -189,8 +191,10 @@ export class RaidManager
await this.userManager.incrementCurrency(usr, raid.tier, 1) await this.userManager.incrementCurrency(usr, raid.tier, 1)
} }
const THREE_DAYS = 259200000;
await Promise.all([ await Promise.all([
...archived.participants.bench.map(giveCurrency), ...archived.participants.bench.filter(signup => Number(signup.timestamp) < (Number(archived.start)-THREE_DAYS)).map(giveCurrency),
...Object.values(archived.participants).flat().map((b: Signup & Character & Spec) => giveCurrency(b)) ...Object.values(archived.participants).flat().map((b: Signup & Character & Spec) => giveCurrency(b))
]) ])
@@ -307,6 +311,17 @@ export class RaidManager
.where('id', '=', raid.id) .where('id', '=', raid.id)
.first() .first()
if(!raidInDb){
return {
title : "Not found",
description : "Not found",
start: "0",
size: 0,
tier: null,
... raiddata
}
}
const characterData: (Signup & Character & Spec)[] = await this.admin const characterData: (Signup & Character & Spec)[] = await this.admin
.knex('signups as s') .knex('signups as s')
.select('s.id as id', 'charactername', 'rank', 'class', 'specid', 'specname', 'race', 'userid', 'benched', 'late', 'raidid', 'characterid', 'specid', 'memo', 'timestamp', 'absent') .select('s.id as id', 'charactername', 'rank', 'class', 'specid', 'specname', 'race', 'userid', 'benched', 'late', 'raidid', 'characterid', 'specid', 'memo', 'timestamp', 'absent')
+3 -1
View File
@@ -132,10 +132,12 @@ export class UserManager
] ]
initialize = async () => { initialize = async () => {
this.exporters = [this, this.guild, this.item, this.raid, this.character] this.exporters = [this.guild, this.item, this.raid, this.character]
//set up permissions //set up permissions
getLogger('UserManager#initialize').debug('setting up permissions') getLogger('UserManager#initialize').debug('setting up permissions')
console.log(this.character.RPCFeatures())
await Promise.all( await Promise.all(
[this, ...this.exporters].flatMap(exp => exp.RPCFeatures().map(async (feature) => { [this, ...this.exporters].flatMap(exp => exp.RPCFeatures().map(async (feature) => {
try { try {
+133 -1
View File
@@ -423,6 +423,138 @@ export const ZGMounts = [
"Primal Hakkari Idol" "Primal Hakkari Idol"
] ]
export const Naxx = [
"Band of Unanswered Prayers",
"Wristguards of Vengeance",
"Gem of Nerubis",
"Touch of Frost",
"Cryptfiend Silk Cloak",
"Claymore of Unholy Might",
"Digested Hand of Power",
"Gluth's Missing Collar",
"Death's Bargain",
"Rime Covered Mantle",
"Polar Helmet",
"Glacial Headdress",
"Sadist's Collar",
"The Soul Harvester's Bindings",
"Boots of Displacement",
"Widow's Remorse",
"Malice Stone Pendant",
"The Widow's Embrace",
"Icebane Pauldrons",
"Polar Shoulder Pads",
"Midnight Haze",
"Icy Scale Spaulders",
"Toxin Injector",
"Glacial Mantle",
"The End of Dreams",
"Icebane Helmet",
"Legplates of Carnage",
"Icy Scale Coif",
"Necklace of Necropsy",
"Preceptor's Hat",
"Girdle of the Mentor",
"Wand of the Whispering Dead",
"Idol of Longevity",
"Veil of Eclipse",
"Signet of the Fallen Defender",
"Iblis, Blade of the Fallen Seraph",
"Doomfinger", "Nerubian Slavemaker",
"Soulseeker",
"Hammer of the Twisting Nether",
"The Hungering Cold",
"Stormrage's Talisman of Seething",
"Gressil, Dawn of Ruin",
"Kingsfall",
"Might of Menethil",
"Gem of Trapped Innocents",
"The Phylactery of Kel'Thuzad",
"Shield of Condemnation",
"Brimstone Staff",
"The Eye of Nerub",
"Band of Unnatural Forces",
"Loatheb's Reflection",
"Ring of Spiritual Fervor",
"Maexxna's Fang",
"Crystal Webbed Robe",
"Kiss of the Spider",
"Wraith Blade",
"Pendant of Forgotten Names",
"Cloak of the Scourge",
"Hatchet of Sundered Bone",
"Band of the Inevitable",
"Noth's Frigid Heart",
"Totem of Flowing Water",
"Libram of Light",
"Hailstone Band",
"The Plague Bearer",
"Cloak of Suturing",
"Severance",
"Band of Reanimation",
"Wand of Fates",
"The Face of Death",
"Claw of the Frost Wyrm",
"Fortitude of the Scourge",
"Eye of the Dead",
"Sapphiron's Right Eye",
"The Restrained Essence of Sapphiron",
"Shroud of Dominion",
"Slayer's Crest",
"Resilience of the Scourge",
"Cloak of the Necropolis",
"Sapphiron's Left Eye",
"Power of the Scourge",
"Might of the Scourge",
"Spire of Twilight",
"Eye of Diminution",
"Leggings of Polarity",
"Plated Abomination Ribcage",
"The Castigator",
"Leggings of Apocalypse",
"Maul of the Redeemed Crusader",
"Seal of the Damned",
"Soulstring",
"Warmth of Forgiveness",
"Corrupted Ashbringer",
"Pauldrons of Elemental Fury",
"Girdle of Elemental Fury",
"Leggings of Elemental Fury",
"Belt of the Grand Crusader",
"Spaulders of the Grand Crusader",
"Ring of the Eternal Flame",
"Stygian Buckler",
"Misplaced Servo Arm",
"Leggings of the Grand Crusader",
"Harbinger of Doom",
"Necro-Knight's Garb",
"Ghoul Skin Tunic",
"Desecrated Circlet",
"Desecrated Robe",
"Desecrated Shoulderpads",
"Desecrated Sandals",
"Desecrated Bindings",
"Desecrated Wristguards",
"Desecrated Bracers",
"Desecrated Spaulders",
"Desecrated Gloves",
"Desecrated Belt",
"Desecrated Headpiece",
"Desecrated Boots",
"Desecrated Pauldrons",
"Desecrated Tunic",
"Desecrated Leggings",
"Desecrated Helmet",
"Desecrated Legplates",
"Desecrated Handguards",
"Desecrated Sabatons",
"Desecrated Girdle",
"Desecrated Legguards",
"Desecrated Gauntlets",
"Desecrated Waistguard",
"Desecrated Breastplate"
]
export type AllItems = { export type AllItems = {
[tier in Tiers]: string[] [tier in Tiers]: string[]
} }
@@ -433,5 +565,5 @@ export const allItems: AllItems = {
ZG: ZGMounts, ZG: ZGMounts,
AQ20: [], AQ20: [],
AQ40: AQ40, AQ40: AQ40,
Naxx: [] Naxx: Naxx
} }
+121 -32
View File
@@ -2730,18 +2730,18 @@
"dev": true "dev": true
}, },
"@types/socket.io": { "@types/socket.io": {
"version": "2.1.10", "version": "2.1.11",
"resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.10.tgz", "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.11.tgz",
"integrity": "sha512-1fQMaDU/x2LPljEI/QI5IKl8sBYHM/zv32YYKvNrVEor7/1+MLqMqmWt8Bb8Vpf+PlIPBiTTC0BnrRx7ju3xOw==", "integrity": "sha512-bVprmqPhJMLb9ZCm8g0Xy8kwBFRbnanOWSxzWkDkkIwxTvud5tKMfAJymXX6LQbizUKCS1yima7JM4BeLqjNqA==",
"requires": { "requires": {
"@types/engine.io": "*", "@types/engine.io": "*",
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/socket.io-client": { "@types/socket.io-client": {
"version": "1.4.33", "version": "1.4.34",
"resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.33.tgz", "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.34.tgz",
"integrity": "sha512-m4LnxkljsI9fMsjwpW5QhRpMixo2BeeLpFmg0AE+sS4H1pzAd/cs/ftTiL60FLZgfFa8PFRPx5KsHu8O0bADKQ==" "integrity": "sha512-Lzia5OTQFJZJ5R4HsEEldywiiqT9+W2rDbyHJiiTGqOcju89sCsQ8aUXDljY6Ls33wKZZGC0bfMhr/VpOyjtXg=="
}, },
"@types/source-list-map": { "@types/source-list-map": {
"version": "0.1.2", "version": "0.1.2",
@@ -3766,7 +3766,8 @@
"base64-arraybuffer": { "base64-arraybuffer": {
"version": "0.1.5", "version": "0.1.5",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
"dev": true
}, },
"base64-js": { "base64-js": {
"version": "1.3.1", "version": "1.3.1",
@@ -15789,9 +15790,9 @@
"integrity": "sha512-ZYzRkETgBrdEGzL5JSKimvjI2CX7ioyZCkX2BpcfyjqI+079W0wHAyj5W4rIZMcDSOHgLZtgz1IdDi/vU77KEQ==" "integrity": "sha512-ZYzRkETgBrdEGzL5JSKimvjI2CX7ioyZCkX2BpcfyjqI+079W0wHAyj5W4rIZMcDSOHgLZtgz1IdDi/vU77KEQ=="
}, },
"rpclibrary": { "rpclibrary": {
"version": "2.0.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-2.0.1.tgz", "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-2.3.1.tgz",
"integrity": "sha512-phdU0TkrnNVXdFa5PKkbWvn+MxocowvqalEUG1jfpkc1CoQkJ9AXa4OXSEu5r2a2IFhVauYva/qXv1em5yrHSg==", "integrity": "sha512-y6F6gYxm9joJcnyxNeEoUSAbYGsaogcEKb1bxVB97+HcT4J6q9yw2fGnWS1FeeAUbTLUwASdlqJvRLPLiMPlcQ==",
"requires": { "requires": {
"@types/socket.io": "^2.1.8", "@types/socket.io": "^2.1.8",
"@types/socket.io-client": "^1.4.33", "@types/socket.io-client": "^1.4.33",
@@ -15806,6 +15807,11 @@
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
}, },
"base64-arraybuffer": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
"integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
},
"base64id": { "base64id": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
@@ -15840,18 +15846,18 @@
} }
}, },
"engine.io-client": { "engine.io-client": {
"version": "3.4.3", "version": "3.4.4",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.3.tgz", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz",
"integrity": "sha512-0NGY+9hioejTEJCaSJZfWZLk4FPI9dN+1H1C4+wj2iuFba47UgZbJzfWs4aNFajnX/qAaYKbe2lLTfEEWzCmcw==", "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==",
"requires": { "requires": {
"component-emitter": "~1.3.0", "component-emitter": "~1.3.0",
"component-inherit": "0.0.3", "component-inherit": "0.0.3",
"debug": "~4.1.0", "debug": "~3.1.0",
"engine.io-parser": "~2.2.0", "engine.io-parser": "~2.2.0",
"has-cors": "1.1.0", "has-cors": "1.1.0",
"indexof": "0.0.1", "indexof": "0.0.1",
"parseqs": "0.0.5", "parseqs": "0.0.6",
"parseuri": "0.0.5", "parseuri": "0.0.6",
"ws": "~6.1.0", "ws": "~6.1.0",
"xmlhttprequest-ssl": "~1.5.4", "xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2" "yeast": "0.1.2"
@@ -15862,6 +15868,24 @@
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
}, },
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"ws": { "ws": {
"version": "6.1.4", "version": "6.1.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
@@ -15873,13 +15897,13 @@
} }
}, },
"engine.io-parser": { "engine.io-parser": {
"version": "2.2.0", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
"integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
"requires": { "requires": {
"after": "0.8.2", "after": "0.8.2",
"arraybuffer.slice": "~0.0.7", "arraybuffer.slice": "~0.0.7",
"base64-arraybuffer": "0.1.5", "base64-arraybuffer": "0.1.4",
"blob": "0.0.5", "blob": "0.0.5",
"has-binary2": "~1.0.2" "has-binary2": "~1.0.2"
} }
@@ -15905,12 +15929,12 @@
"socket.io-adapter": "~1.1.0", "socket.io-adapter": "~1.1.0",
"socket.io-client": "2.3.0", "socket.io-client": "2.3.0",
"socket.io-parser": "~3.4.0" "socket.io-parser": "~3.4.0"
}
}, },
"socket.io-adapter": { "dependencies": {
"version": "1.1.2", "base64-arraybuffer": {
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", "version": "0.1.5",
"integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
}, },
"socket.io-client": { "socket.io-client": {
"version": "2.3.0", "version": "2.3.0",
@@ -15934,15 +15958,20 @@
}, },
"dependencies": { "dependencies": {
"socket.io-parser": { "socket.io-parser": {
"version": "3.3.0", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz",
"integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", "integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==",
"requires": { "requires": {
"component-emitter": "1.2.1", "component-emitter": "~1.3.0",
"debug": "~3.1.0", "debug": "~3.1.0",
"isarray": "2.0.1" "isarray": "2.0.1"
}, },
"dependencies": { "dependencies": {
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"debug": { "debug": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
@@ -15954,6 +15983,66 @@
} }
} }
} }
}
}
},
"socket.io-adapter": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
"integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
},
"socket.io-client": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.1.tgz",
"integrity": "sha512-YXmXn3pA8abPOY//JtYxou95Ihvzmg8U6kQyolArkIyLd0pgVhrfor/iMsox8cn07WCOOvvuJ6XKegzIucPutQ==",
"requires": {
"backo2": "1.0.2",
"component-bind": "1.0.0",
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"engine.io-client": "~3.4.0",
"has-binary2": "~1.0.2",
"indexof": "0.0.1",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
},
"dependencies": {
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"socket.io-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz",
"integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==",
"requires": {
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"isarray": "2.0.1"
}
}
}
}, },
"socket.io-parser": { "socket.io-parser": {
"version": "3.4.1", "version": "3.4.1",
@@ -15966,9 +16055,9 @@
} }
}, },
"ws": { "ws": {
"version": "7.3.1", "version": "7.4.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz",
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ=="
}, },
"xmlhttprequest-ssl": { "xmlhttprequest-ssl": {
"version": "1.5.5", "version": "1.5.5",
+1 -1
View File
@@ -79,7 +79,7 @@
"normalize.css": "6.0.0", "normalize.css": "6.0.0",
"pace-js": "1.0.2", "pace-js": "1.0.2",
"roboto-fontface": "0.8.0", "roboto-fontface": "0.8.0",
"rpclibrary": "^2.0.1", "rpclibrary": "^2.3.1",
"rxjs": "6.5.2", "rxjs": "6.5.2",
"rxjs-compat": "6.3.0", "rxjs-compat": "6.3.0",
"socicon": "3.0.5", "socicon": "3.0.5",
+20 -11
View File
@@ -15,15 +15,21 @@ global['alert'] = console.log
global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest; global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest;
global['fetch'] = fetch.default; global['fetch'] = fetch.default;
/**
* Function to attach this SSR application to an express server. Exported as function due to webpack inconsistencies.
export async function attachExpress(app, staticDir = "./dist", logger = console) { *
* @param app express.Application to run on
* @param staticDir Where the static website content is (i.e. ./dist)
* @param logger A loggerservice or console
* @param rateLimiter a rate-limiter-flexible instance
*/
export async function attachExpress(app, staticDir, logger, rateLimiter) {
const STATIC_FOLDER = resolve(process.cwd(), staticDir); const STATIC_FOLDER = resolve(process.cwd(), staticDir);
enableProdMode(); enableProdMode();
const loggerService = { const loggerService = {
warn: (...args) => logger.error(...args), warn: (...args) => logger.warn(...args),
error: (...args) => logger.warn(...args), error: (...args) => logger.error(...args),
log: (...args) => logger.info(...args), log: (...args) => logger.info(...args),
table: (...args) => logger.info(...args), table: (...args) => logger.info(...args),
collapsed: (msg, type, ...args) => loggerService[type](msg, ...args) collapsed: (msg, type, ...args) => loggerService[type](msg, ...args)
@@ -57,11 +63,14 @@ export async function attachExpress(app, staticDir = "./dist", logger = console)
); );
app.get('*', (req, res) => { app.get('*', (req, res) => {
//console.log('*', req.path); rateLimiter.consume(req.ip, 3)
try{ .then(_ => {
res.render(resolve(STATIC_FOLDER, 'browser/index'), { req, res }) res.render(resolve(STATIC_FOLDER, 'browser/index'), { req, res })
}catch(e){ })
loggerService.error(e) .catch(_ => {
} loggerService.error(String(new Error("429 BAD REQUEST "+req.path+" FROM "+req.ip)))
}); res.send("429 Too many requests")
res.status(429)
})
})
} }
@@ -21,12 +21,12 @@ export class RegisterComponent implements OnInit{
race: "Night Elf" race: "Night Elf"
} }
ranks = _Rank.splice(5) //remove admin, gm, officer ranks = ["Trial"] //remove admin, gm, officer
classes = _Class classes = _Class
races = _Race races = _Race
selectedClass : Class = "Warrior" selectedClass : Class = "Warrior"
selectedSpec = specs[this.selectedClass][0]
specs = specs[this.selectedClass] specs = specs[this.selectedClass]
selectedSpec = specs[this.selectedClass][0]
showApplication = false showApplication = false
submitted = false submitted = false
@@ -1,8 +1,8 @@
<nb-card <nb-card class="col-12 col-xl-9">
class = "col-12 col-xl-9">
<nb-card-header style="text-transform: capitalize;"> <nb-card-header style="text-transform: capitalize;">
<h4> <h4>
<a *ngIf="link === 'character'" [ngStyle]="{'color': color}" [routerLink]="'/frontcraft/character/'+char.charactername"> <a *ngIf="link === 'character'" [ngStyle]="{'color': color}"
[routerLink]="'/frontcraft/character/'+char.charactername">
{{char.charactername}} {{char.charactername}}
</a> </a>
</h4> </h4>
@@ -14,10 +14,31 @@ class = "col-12 col-xl-9">
</nb-card-header> </nb-card-header>
<nb-card-body> <nb-card-body>
<span *ngIf="!canManage">
<img style="width:20px; height:20px" [src]="'../../../../assets/images/'+char.race.toLowerCase()+'_xs.gif'" /> <img style="width:20px; height:20px" [src]="'../../../../assets/images/'+char.race.toLowerCase()+'_xs.gif'" />
{{char.race}}<br /> {{char.race}}<br />
<img style="width:20px; height:20px" [src]="'../../../../assets/images/'+char.class.toLowerCase()+'.png'" /> <img style="width:20px; height:20px" [src]="'../../../../assets/images/'+char.class.toLowerCase()+'.png'" />
{{char.specname}} {{char.class}}<br /> {{char.specname}} {{char.class}}<br />
</span>
<span *ngIf="canManage">
<input nbInput [required]="true" name="preMember" [(ngModel)]="char.charactername"><br />
<nb-select [(selected)]="char.race" placeholder="Race">
<nb-option *ngFor="let race of races" [value]="race">{{race}}</nb-option>
</nb-select>
<nb-select [(selected)]="char.specname" placeholder="Spec">
<nb-option *ngFor="let spec of specs" [value]="spec">{{spec}}</nb-option>
</nb-select>
<nb-select [(selected)]="char.class" placeholder="Class" (selectedChange)="onSelectClass()">
<nb-option *ngFor="let class of classes" [value]="class">{{class}}</nb-option>
</nb-select>
<button nbButton
status="primary"
[class.btn-pulse]="submitted"
(click)="updateCharacter()">
Update
</button>
</span>
<span *ngIf="link === 'owner'"> <span *ngIf="link === 'owner'">
Owned by <a [routerLink]="'/frontcraft/user/'+char.username"> {{char.username}} ({{char.rank}})</a> Owned by <a [routerLink]="'/frontcraft/user/'+char.username"> {{char.username}} ({{char.rank}})</a>
</span> </span>
@@ -1,9 +1,10 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, OnInit, Input } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Spec, User, Character, Item } from '../../../../../../backend/Types/Types'; import { Spec, User, Character, Item, _Class, _Race, Class } from '../../../../../../backend/Types/Types';
import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; import { getClassColor, specs } from '../../../../../../backend/Types/PlayerSpecs';
import { _Tiers } from '../../../../../../backend/Types/Items'; import { _Tiers } from '../../../../../../backend/Types/Items';
import { IApiService } from '../../services/ApiService/ApiService'; import { IApiService } from '../../services/ApiService/ApiService';
import { NbToastrService } from '@nebular/theme';
@Component({ @Component({
selector: 'character', selector: 'character',
@@ -13,11 +14,17 @@ export class FrontcraftCharacterComponent implements OnInit {
@Input() name?: string @Input() name?: string
@Input() link?: "owner" | "character" = 'owner' @Input() link?: "owner" | "character" = 'owner'
char: (Character & User & Spec) = { char: (Character & User & Spec) = {
charactername: "Loading",
race: 'Human', race: 'Human',
class: 'Warrior' class: 'Warrior',
spec: 'Arms'
} as any } as any
classes = _Class
races = _Race
specs = specs[this.char.class]
color: string color: string
tokens tokens
dataLink = "" dataLink = ""
@@ -27,23 +34,32 @@ export class FrontcraftCharacterComponent implements OnInit {
boss2 = { name: undefined, date: undefined } boss2 = { name: undefined, date: undefined }
gear2: Item[] = [] gear2: Item[] = []
canManage: boolean = false
constructor( constructor(
private api: IApiService, private api: IApiService,
private route: ActivatedRoute, private route: ActivatedRoute,
private toast: NbToastrService,
) { } ) { }
async ngOnInit() { async ngOnInit() {
const param = this.name || this.route.snapshot.paramMap.get('name'); const param = this.name || this.route.snapshot.paramMap.get('name');
if (!param) return if (!param) return
const manageChar = this.api.get('manageCharacter')
if (manageChar) this.canManage = true
const char = await this.api.get('CharacterManager').getCharacterByName(param) const char = await this.api.get('CharacterManager').getCharacterByName(param)
if (char) { if (char) {
this.color = getClassColor(char.class) this.color = getClassColor(char.class)
this.char = char this.char = char
this.specs = specs[this.char.class]
this.api.get('ItemManager').getTokens(this.char, _Tiers, true).then(tokens => { this.api.get('ItemManager').getTokens(this.char, _Tiers, true).then(tokens => {
this.tokens = tokens this.tokens = tokens
}) })
/*
this.dataLink = "https://classic.warcraftlogs.com:443/v1/parses/character/" + this.char.charactername.replace(/^\w/, c => c.toUpperCase()) + "/Gandling/EU?api_key=c698515ab4f592cdb848d80b3abe616c&metric=dps" this.dataLink = "https://classic.warcraftlogs.com:443/v1/parses/character/" + this.char.charactername.replace(/^\w/, c => c.toUpperCase()) + "/Gandling/EU?api_key=c698515ab4f592cdb848d80b3abe616c&metric=dps"
const json: any[] = await fetch(this.dataLink).then(raw => raw.json()) const json: any[] = await fetch(this.dataLink).then(raw => raw.json())
@@ -68,6 +84,24 @@ export class FrontcraftCharacterComponent implements OnInit {
const maybeItems: (Item | undefined)[] = await Promise.all(data.gear.filter(item => item.name != "Unknown Item").map(async item => await this.api.get('ItemManager').getItem(item.name))) const maybeItems: (Item | undefined)[] = await Promise.all(data.gear.filter(item => item.name != "Unknown Item").map(async item => await this.api.get('ItemManager').getItem(item.name)))
const geardata = maybeItems.filter(maybeItem => maybeItem != null) const geardata = maybeItems.filter(maybeItem => maybeItem != null)
this.gear2 = geardata this.gear2 = geardata
*/
} }
} }
updateCharacter = async () => {
const manager = this.api.get('manageCharacter')
if (manager) {
const specid = await this.api.get('CharacterManager').getSpecId(this.char['class'], this.char['specname'] as any)
this.char.specid = specid
manager.updateCharacter(this.char).then(() => {
this.toast.show('Update', 'Success', { status: 'success' })
})
}
}
onSelectClass() {
this.specs = specs[this.char.class]
setTimeout(() => {
this.char.specname = this.specs[0]
}, 25)
}
} }
@@ -72,6 +72,10 @@
<nb-card-header>Tanks ({{raid.tanks.length}})</nb-card-header> <nb-card-header>Tanks ({{raid.tanks.length}})</nb-card-header>
<nb-card-body> <nb-card-body>
<div *ngFor="let participant of raid.tanks"> <div *ngFor="let participant of raid.tanks">
<button (click)="adminSetAbsent(participant)" *ngIf="manageRaid" nbButton outline
status="info" size="tiny">
A
</button>
<button (click)="setBench(participant)" *ngIf="manageRaid" nbButton outline <button (click)="setBench(participant)" *ngIf="manageRaid" nbButton outline
status="warning" size="tiny"> status="warning" size="tiny">
B B
@@ -122,6 +126,10 @@
<nb-card-header>Healers ({{raid.healers.length}})</nb-card-header> <nb-card-header>Healers ({{raid.healers.length}})</nb-card-header>
<nb-card-body> <nb-card-body>
<div *ngFor="let participant of raid.healers"> <div *ngFor="let participant of raid.healers">
<button (click)="adminSetAbsent(participant)" *ngIf="manageRaid" nbButton
outline status="info" size="tiny">
A
</button>
<button (click)="setBench(participant)" *ngIf="manageRaid" nbButton outline <button (click)="setBench(participant)" *ngIf="manageRaid" nbButton outline
status="warning" size="tiny"> status="warning" size="tiny">
B B
@@ -171,6 +179,10 @@
<nb-card-header>{{group.key}} ({{group.value.length}})</nb-card-header> <nb-card-header>{{group.key}} ({{group.value.length}})</nb-card-header>
<nb-card-body> <nb-card-body>
<div *ngFor="let participant of group.value"> <div *ngFor="let participant of group.value">
<button (click)="adminSetAbsent(participant)" *ngIf="manageRaid" nbButton
outline status="info" size="tiny">
A
</button>
<button (click)="setBench(participant)" *ngIf="manageRaid" nbButton outline <button (click)="setBench(participant)" *ngIf="manageRaid" nbButton outline
status="warning" size="tiny"> status="warning" size="tiny">
B B
@@ -206,8 +218,9 @@
</p> </p>
</div> </div>
</ng-template> </ng-template>
<nb-icon *ngIf="participant.memo" [nbPopover]="memoBox" nbPopoverTrigger="hover" <nb-icon *ngIf="participant.memo" [nbPopover]="memoBox"
style="width: 0.75em; height: 0.75em;" icon="message-circle-outline"> nbPopoverTrigger="hover" style="width: 0.75em; height: 0.75em;"
icon="message-circle-outline">
</nb-icon> </nb-icon>
<span *ngIf="participant.rank=='Trial'" style="font-size: 9px;"> <span *ngIf="participant.rank=='Trial'" style="font-size: 9px;">
Trial Trial
@@ -244,7 +257,6 @@
<tr> <tr>
<td> <td>
<wowhead [item]="item.value[0]"></wowhead><br /> <wowhead [item]="item.value[0]"></wowhead><br />
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -254,7 +266,15 @@
<span <span
[ngStyle]="{'text-decoration': token.rank=='Trial'? 'line-through' : 'none currentcolor solid' }"> [ngStyle]="{'text-decoration': token.rank=='Trial'? 'line-through' : 'none currentcolor solid' }">
<span style="text-transform: capitalize;"> <span style="text-transform: capitalize;">
<button (click)="decreaseReserve(token)" *ngIf="changeReserve" nbButton
outline status="warning" size="tiny">
-
</button>
[ {{token.level}} ]&nbsp; [ {{token.level}} ]&nbsp;
<button (click)="increaseReserve(token)" *ngIf="changeReserve" nbButton
outline status="warning" size="tiny">
+
</button>
<a [routerLink]="'/frontcraft/character/'+token.charactername" <a [routerLink]="'/frontcraft/character/'+token.charactername"
[ngStyle]="{'color':token.level>=10?'#ff8000':token.level>=8?'#a335ee':token.level>=6?'#0070dd':token.level>=4?'#1eff00':token.level>=2?'#ffffff':'#9d9d9d'}"> [ngStyle]="{'color':token.level>=10?'#ff8000':token.level>=8?'#a335ee':token.level>=6?'#0070dd':token.level>=4?'#1eff00':token.level>=2?'#ffffff':'#9d9d9d'}">
{{token.charactername}} {{token.charactername}}
@@ -26,6 +26,7 @@ export class FrontcraftRaidComponent
isTier = false isTier = false
reservesShown = true reservesShown = true
manageRaid manageRaid
changeReserve
mySignup: (Signup & Character & Spec) mySignup: (Signup & Character & Spec)
raid: RaidData = <any>{ raid: RaidData = <any>{
@@ -64,6 +65,7 @@ export class FrontcraftRaidComponent
ngOnInit() { ngOnInit() {
this.manageRaid = this.api.get('manageRaid') this.manageRaid = this.api.get('manageRaid')
this.changeReserve = this.api.get('changeReserve')
const signupFeature = this.api.get('signup') const signupFeature = this.api.get('signup')
if (signupFeature) { if (signupFeature) {
@@ -187,7 +189,6 @@ export class FrontcraftRaidComponent
} }
display = async (raiddata: RaidData) => { display = async (raiddata: RaidData) => {
this.logger.log(raiddata)
this.isTier = allItems[raiddata.tier] != null this.isTier = allItems[raiddata.tier] != null
this.raid = raiddata this.raid = raiddata
@@ -248,4 +249,31 @@ export class FrontcraftRaidComponent
.then(_ => this.refresh()) .then(_ => this.refresh())
} }
} }
adminSetAbsent(signup: Signup) {
const manageRaid = this.api.get('manageRaid')
if(manageRaid){
const signupUpdated:Signup = {
benched: false,
absent: !signup.absent,
late: false,
characterid: signup.characterid,
raidid: signup.raidid,
timestamp: signup.timestamp,
id: signup.id,
memo: signup.memo
}
manageRaid.updateSignup(signupUpdated)
.then(_ => this.refresh())
}
}
increaseReserve(token: Character & SRToken & Item){
this.changeReserve.increaseReserve(token, this.raid.id)
}
decreaseReserve(token: Character & SRToken & Item){
this.changeReserve.decreaseReserve(token, this.raid.id)
}
} }
@@ -31,7 +31,7 @@ export class ClientApiService implements IApiService{
try{ try{
const sock = new RPCSocket( const sock = new RPCSocket(
auth.port, auth.port,
window.location.hostname window.location.hostname,
) )
sock.hook('kick', () => { sock.hook('kick', () => {
@@ -48,7 +48,7 @@ export class ClientApiService implements IApiService{
sock.on('error', async (e) => { sock.on('error', async (e) => {
this.logger.log("priv#error", e); this.logger.log("priv#error", e);
await this.logout()
//handled via unprivileged socket //handled via unprivileged socket
}) })
const authSock = await sock.connect(auth.token.value) const authSock = await sock.connect(auth.token.value)
@@ -95,7 +95,6 @@ export class ClientApiService implements IApiService{
throw new Error("Login failed") throw new Error("Login failed")
} }
//attach error handler now so failed logins dont trigger reloads
this.socket.on('error', async (err) => { this.socket.on('error', async (err) => {
location.reload() location.reload()
}) })