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/**/*
!test/**/*
!migrations/**/*
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": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"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": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.4.tgz",
@@ -107,6 +124,27 @@
"@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": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.0.0.tgz",
@@ -133,6 +171,11 @@
"@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": {
"version": "5.2.7",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
@@ -151,6 +194,25 @@
"@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": {
"version": "2.1.10",
"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",
"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": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
@@ -4410,9 +4477,9 @@
}
},
"rpclibrary": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-2.0.1.tgz",
"integrity": "sha512-phdU0TkrnNVXdFa5PKkbWvn+MxocowvqalEUG1jfpkc1CoQkJ9AXa4OXSEu5r2a2IFhVauYva/qXv1em5yrHSg==",
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-2.3.1.tgz",
"integrity": "sha512-y6F6gYxm9joJcnyxNeEoUSAbYGsaogcEKb1bxVB97+HcT4J6q9yw2fGnWS1FeeAUbTLUwASdlqJvRLPLiMPlcQ==",
"requires": {
"@types/socket.io": "^2.1.8",
"@types/socket.io-client": "^1.4.33",
+3 -1
View File
@@ -25,6 +25,7 @@
"author": "frontblock.me",
"license": "ISC",
"dependencies": {
"@types/express": "^4.17.8",
"@types/mocha": "^5.2.7",
"@types/node": "^14.0.27",
"bsert": "0.0.10",
@@ -46,9 +47,10 @@
"ngx-cookie-service": "^2.4.0",
"node-fetch": "^2.6.0",
"path": "^0.12.7",
"rate-limiter-flexible": "^2.1.10",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.0",
"rpclibrary": "^2.0.1",
"rpclibrary": "^2.3.1",
"simple-git": "^1.124.0",
"spawn-sync": "^2.0.0",
"sqlite3": "^4.2.0",
+42 -23
View File
@@ -15,12 +15,14 @@ import { CharacterManager } from '../Components/Character/CharacterManager';
import { UserManager } from '../Components/User/UserManager';
import { RootComponent } from '../Injector/ServiceDecorator';
import { TableDefinitionExporter } from '../Types/Interfaces';
import { AdminConf, TableDefiniton } from '../Types/Types';
import { AdminConf, TableDefiniton, SomeOf } from '../Types/Types';
import { RPCConfigLoader } from '../Components/RPCConfigLoader';
import { FrontworkComponent } from '../Types/FrontworkComponent';
import { IAdmin } from './Interface';
import { Injector } from '../Injector/Injector';
import { PubSub } from '../Components/PubSub/PubSub';
import { BurstyRateLimiter, RateLimiterMemory, RateLimiterAbstract } from 'rate-limiter-flexible'
@RootComponent({
injectable: IAdmin,
@@ -39,7 +41,7 @@ export class FrontworkAdmin
config: RPCConfigLoader<AdminConf>
rpcServer: RPCServer
private express
private express : express.Application
private httpServer
constructor(private frontworkComponents: FrontworkComponent[] = []) {
@@ -66,14 +68,26 @@ export class FrontworkAdmin
}
async start() {
const ONE_REQUEST = 15
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()
const app = await this.makeExpress()
const app = await this.makeExpress(rateLimiter)
const httpServer = new http.Server(app)
await this.startWebsocket(httpServer)
httpServer.listen(port)
await this.attachAngularSSR(app)
await this.attachAngularSSR(app, rateLimiter)
getLogger('Admin#startWebsocket').debug("Webserver up on", port)
await Promise.all(this.frontworkComponents.map(c => c.initialize ? c.initialize() : undefined))
@@ -125,48 +139,53 @@ export class FrontworkAdmin
}
], {
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)
}
private async makeExpress() {
private async makeExpress(rateLimiter : SomeOf<RateLimiterAbstract> = new RateLimiterMemory({ points: 6, duration: 3 })) {
if (this.httpServer != null || this.express != null) {
getLogger('Admin#startWebserver').warn("Webserver is already running")
return
}
this.express = express()
const distFolder = "../../../../dist"
this.express.get('*.*', (req, res) => {
//console.log('*.*', req.path);
try{
const filepath = Path.join(__dirname, distFolder, 'browser', decodeURIComponent(req.path))
if(!existsSync(filepath)){
throw new Error("Bad file access: "+filepath)
}
res.sendFile(filepath)
res.status(200)
}catch(e){
getLogger('Admin#startWebserver#serveFile').error(String(e))
res.send("404 NOT FOUND")
res.status(404)
}
rateLimiter.consume(req.ip)
.then(_ => {
const filepath = Path.join(__dirname, distFolder, 'browser', decodeURIComponent(req.path))
if(!existsSync(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.status(200)
})
.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
}
attachAngularSSR = async (express : express.Application) => {
attachAngularSSR = async (express : express.Application, rateLimiter : SomeOf<RateLimiterAbstract> = new RateLimiterMemory({ points: 6, duration: 3 })) => {
const distFolder = "../../../../dist"
const ngExpressServer = Path.join(__dirname, distFolder, 'server.js')
let port: number = this.config.getConfig().httpPort
try {
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")
} catch (e) {
getLogger('Admin#startWebserver').error(e)
@@ -30,7 +30,13 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
this.getHeadCount
]
RPCFeatures = () => []
RPCFeatures = () => [{
name: 'manageCharacter' as 'manageCharacter',
RPCs: [
this.updateCharacter
]
}]
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> => {
try{
character.charactername = character.charactername.toLowerCase()
@@ -10,4 +10,6 @@ export class ICharacterManager{
getCharactersOfUser: (username: string) => Promise<(Character & Spec)[]>
getUserOfCharacter: (character:Character) => Promise<User>
getHeadCount: (clazz: Class) => Promise<number>
updateCharacter: (character: Character) => Promise<Character>
}
@@ -14,5 +14,7 @@ export type CharacterManagerIfc = {
}
export type CharacterManagerFeatureIfc = {
manageCharacter: {
updateCharacter: ICharacterManager['updateCharacter']
}
}
+3
View File
@@ -15,4 +15,7 @@ export class IItemManager{
wipeCurrencyAndItems: () => Promise<void>
decayTokens: (tier: Tiers, 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: () => [
this.decayTokensOfCharacter
]
}, {
name: 'changeReserve' as 'changeReserve',
RPCs: () => [
this.decreaseReserve,
this.increaseReserve
]
}]
notifyRaid = async (raid: Raid | { id: number }) => {
@@ -253,7 +259,7 @@ export class ItemManager
itemname: item.itemname
})
} else {
getLogger('ItemManager').debug('new reserve')
//getLogger('ItemManager').debug('new reserve')
await this.admin
.knex(item.tier + 'tokens')
.transacting(tx)
@@ -374,7 +380,7 @@ export class ItemManager
}
decayTokens = async (tier: Tiers, amount = 1) => {
getLogger('ItemManager#decayTokens').debug(tier)
//getLogger('ItemManager#decayTokens').debug(tier)
await this.admin
.knex(tier + 'tokens')
@@ -387,7 +393,7 @@ export class ItemManager
}
decayTokensOfCharacter = async (tier: Tiers, character:Character) => {
getLogger('ItemManager#decayTokensOfCharacter').debug(character, tier)
//getLogger('ItemManager#decayTokensOfCharacter').debug(character, tier)
await this.admin
.knex(tier + 'tokens')
.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){
@@ -23,4 +23,8 @@ export type ItemManagerFeatureIfc = {
decay: {
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>
adminUnsign: (character:Character, raid:Raid) => Promise<any>
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 { getLogger } from "log4js";
@Injectable(IRaidManager)
export class RaidManager
implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManager {
@@ -27,7 +29,7 @@ export class RaidManager
@Inject(ICharacterManager)
private characterManager: ICharacterManager
@Inject(ItemManager)
@Inject(IItemManager)
private itemManager: IItemManager
@Inject(IPubSub)
@@ -189,8 +191,10 @@ export class RaidManager
await this.userManager.incrementCurrency(usr, raid.tier, 1)
}
const THREE_DAYS = 259200000;
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))
])
@@ -307,6 +311,17 @@ export class RaidManager
.where('id', '=', raid.id)
.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
.knex('signups as s')
.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 () => {
this.exporters = [this, this.guild, this.item, this.raid, this.character]
this.exporters = [this.guild, this.item, this.raid, this.character]
//set up permissions
getLogger('UserManager#initialize').debug('setting up permissions')
console.log(this.character.RPCFeatures())
await Promise.all(
[this, ...this.exporters].flatMap(exp => exp.RPCFeatures().map(async (feature) => {
try {
+133 -1
View File
@@ -423,6 +423,138 @@ export const ZGMounts = [
"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 = {
[tier in Tiers]: string[]
}
@@ -433,5 +565,5 @@ export const allItems: AllItems = {
ZG: ZGMounts,
AQ20: [],
AQ40: AQ40,
Naxx: []
Naxx: Naxx
}
+137 -48
View File
@@ -2730,18 +2730,18 @@
"dev": true
},
"@types/socket.io": {
"version": "2.1.10",
"resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.10.tgz",
"integrity": "sha512-1fQMaDU/x2LPljEI/QI5IKl8sBYHM/zv32YYKvNrVEor7/1+MLqMqmWt8Bb8Vpf+PlIPBiTTC0BnrRx7ju3xOw==",
"version": "2.1.11",
"resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.11.tgz",
"integrity": "sha512-bVprmqPhJMLb9ZCm8g0Xy8kwBFRbnanOWSxzWkDkkIwxTvud5tKMfAJymXX6LQbizUKCS1yima7JM4BeLqjNqA==",
"requires": {
"@types/engine.io": "*",
"@types/node": "*"
}
},
"@types/socket.io-client": {
"version": "1.4.33",
"resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.33.tgz",
"integrity": "sha512-m4LnxkljsI9fMsjwpW5QhRpMixo2BeeLpFmg0AE+sS4H1pzAd/cs/ftTiL60FLZgfFa8PFRPx5KsHu8O0bADKQ=="
"version": "1.4.34",
"resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.34.tgz",
"integrity": "sha512-Lzia5OTQFJZJ5R4HsEEldywiiqT9+W2rDbyHJiiTGqOcju89sCsQ8aUXDljY6Ls33wKZZGC0bfMhr/VpOyjtXg=="
},
"@types/source-list-map": {
"version": "0.1.2",
@@ -3766,7 +3766,8 @@
"base64-arraybuffer": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
"dev": true
},
"base64-js": {
"version": "1.3.1",
@@ -15789,9 +15790,9 @@
"integrity": "sha512-ZYzRkETgBrdEGzL5JSKimvjI2CX7ioyZCkX2BpcfyjqI+079W0wHAyj5W4rIZMcDSOHgLZtgz1IdDi/vU77KEQ=="
},
"rpclibrary": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-2.0.1.tgz",
"integrity": "sha512-phdU0TkrnNVXdFa5PKkbWvn+MxocowvqalEUG1jfpkc1CoQkJ9AXa4OXSEu5r2a2IFhVauYva/qXv1em5yrHSg==",
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-2.3.1.tgz",
"integrity": "sha512-y6F6gYxm9joJcnyxNeEoUSAbYGsaogcEKb1bxVB97+HcT4J6q9yw2fGnWS1FeeAUbTLUwASdlqJvRLPLiMPlcQ==",
"requires": {
"@types/socket.io": "^2.1.8",
"@types/socket.io-client": "^1.4.33",
@@ -15806,6 +15807,11 @@
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
"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": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
@@ -15840,18 +15846,18 @@
}
},
"engine.io-client": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.3.tgz",
"integrity": "sha512-0NGY+9hioejTEJCaSJZfWZLk4FPI9dN+1H1C4+wj2iuFba47UgZbJzfWs4aNFajnX/qAaYKbe2lLTfEEWzCmcw==",
"version": "3.4.4",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz",
"integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==",
"requires": {
"component-emitter": "~1.3.0",
"component-inherit": "0.0.3",
"debug": "~4.1.0",
"debug": "~3.1.0",
"engine.io-parser": "~2.2.0",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~6.1.0",
"xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2"
@@ -15862,6 +15868,24 @@
"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=="
},
"ws": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
@@ -15873,13 +15897,13 @@
}
},
"engine.io-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
"integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
"integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
"requires": {
"after": "0.8.2",
"arraybuffer.slice": "~0.0.7",
"base64-arraybuffer": "0.1.5",
"base64-arraybuffer": "0.1.4",
"blob": "0.0.5",
"has-binary2": "~1.0.2"
}
@@ -15905,6 +15929,61 @@
"socket.io-adapter": "~1.1.0",
"socket.io-client": "2.3.0",
"socket.io-parser": "~3.4.0"
},
"dependencies": {
"base64-arraybuffer": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
},
"socket.io-client": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
"integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
"requires": {
"backo2": "1.0.2",
"base64-arraybuffer": "0.1.5",
"component-bind": "1.0.0",
"component-emitter": "1.2.1",
"debug": "~4.1.0",
"engine.io-client": "~3.4.0",
"has-binary2": "~1.0.2",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"object-component": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
},
"dependencies": {
"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"
},
"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"
}
}
}
}
}
}
}
},
"socket.io-adapter": {
@@ -15913,44 +15992,54 @@
"integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
},
"socket.io-client": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
"integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
"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",
"base64-arraybuffer": "0.1.5",
"component-bind": "1.0.0",
"component-emitter": "1.2.1",
"debug": "~4.1.0",
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"engine.io-client": "~3.4.0",
"has-binary2": "~1.0.2",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"object-component": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
},
"dependencies": {
"socket.io-parser": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
"integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
"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": {
"component-emitter": "1.2.1",
"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"
},
"dependencies": {
"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"
}
}
}
}
}
@@ -15966,9 +16055,9 @@
}
},
"ws": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz",
"integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ=="
},
"xmlhttprequest-ssl": {
"version": "1.5.5",
+1 -1
View File
@@ -79,7 +79,7 @@
"normalize.css": "6.0.0",
"pace-js": "1.0.2",
"roboto-fontface": "0.8.0",
"rpclibrary": "^2.0.1",
"rpclibrary": "^2.3.1",
"rxjs": "6.5.2",
"rxjs-compat": "6.3.0",
"socicon": "3.0.5",
+20 -11
View File
@@ -15,15 +15,21 @@ global['alert'] = console.log
global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest;
global['fetch'] = fetch.default;
export async function attachExpress(app, staticDir = "./dist", logger = console) {
/**
* Function to attach this SSR application to an express server. Exported as function due to webpack inconsistencies.
*
* @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);
enableProdMode();
const loggerService = {
warn: (...args) => logger.error(...args),
error: (...args) => logger.warn(...args),
warn: (...args) => logger.warn(...args),
error: (...args) => logger.error(...args),
log: (...args) => logger.info(...args),
table: (...args) => logger.info(...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) => {
//console.log('*', req.path);
try{
rateLimiter.consume(req.ip, 3)
.then(_ => {
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"
}
ranks = _Rank.splice(5) //remove admin, gm, officer
ranks = ["Trial"] //remove admin, gm, officer
classes = _Class
races = _Race
selectedClass : Class = "Warrior"
selectedSpec = specs[this.selectedClass][0]
specs = specs[this.selectedClass]
selectedSpec = specs[this.selectedClass][0]
showApplication = false
submitted = false
@@ -1,8 +1,8 @@
<nb-card
class = "col-12 col-xl-9">
<nb-card class="col-12 col-xl-9">
<nb-card-header style="text-transform: capitalize;">
<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}}
</a>
</h4>
@@ -14,10 +14,31 @@ class = "col-12 col-xl-9">
</nb-card-header>
<nb-card-body>
<img style="width:20px; height:20px" [src]="'../../../../assets/images/'+char.race.toLowerCase()+'_xs.gif'" />
{{char.race}}<br />
<img style="width:20px; height:20px" [src]="'../../../../assets/images/'+char.class.toLowerCase()+'.png'" />
{{char.specname}} {{char.class}}<br />
<span *ngIf="!canManage">
<img style="width:20px; height:20px" [src]="'../../../../assets/images/'+char.race.toLowerCase()+'_xs.gif'" />
{{char.race}}<br />
<img style="width:20px; height:20px" [src]="'../../../../assets/images/'+char.class.toLowerCase()+'.png'" />
{{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'">
Owned by <a [routerLink]="'/frontcraft/user/'+char.username"> {{char.username}} ({{char.rank}})</a>
</span>
@@ -1,9 +1,10 @@
import { Component, OnInit, Input } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Spec, User, Character, Item } from '../../../../../../backend/Types/Types';
import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
import { Spec, User, Character, Item, _Class, _Race, Class } from '../../../../../../backend/Types/Types';
import { getClassColor, specs } from '../../../../../../backend/Types/PlayerSpecs';
import { _Tiers } from '../../../../../../backend/Types/Items';
import { IApiService } from '../../services/ApiService/ApiService';
import { NbToastrService } from '@nebular/theme';
@Component({
selector: 'character',
@@ -13,11 +14,17 @@ export class FrontcraftCharacterComponent implements OnInit {
@Input() name?: string
@Input() link?: "owner" | "character" = 'owner'
char: (Character & User & Spec) = {
charactername: "Loading",
race: 'Human',
class: 'Warrior'
class: 'Warrior',
spec: 'Arms'
} as any
classes = _Class
races = _Race
specs = specs[this.char.class]
color: string
tokens
dataLink = ""
@@ -27,23 +34,32 @@ export class FrontcraftCharacterComponent implements OnInit {
boss2 = { name: undefined, date: undefined }
gear2: Item[] = []
canManage: boolean = false
constructor(
private api: IApiService,
private route: ActivatedRoute,
private toast: NbToastrService,
) { }
async ngOnInit() {
const param = this.name || this.route.snapshot.paramMap.get('name');
if (!param) return
const manageChar = this.api.get('manageCharacter')
if (manageChar) this.canManage = true
const char = await this.api.get('CharacterManager').getCharacterByName(param)
if (char) {
this.color = getClassColor(char.class)
this.char = char
this.specs = specs[this.char.class]
this.api.get('ItemManager').getTokens(this.char, _Tiers, true).then(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"
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 geardata = maybeItems.filter(maybeItem => maybeItem != null)
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-body>
<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
status="warning" size="tiny">
B
@@ -122,6 +126,10 @@
<nb-card-header>Healers ({{raid.healers.length}})</nb-card-header>
<nb-card-body>
<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
status="warning" size="tiny">
B
@@ -171,6 +179,10 @@
<nb-card-header>{{group.key}} ({{group.value.length}})</nb-card-header>
<nb-card-body>
<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
status="warning" size="tiny">
B
@@ -206,8 +218,9 @@
</p>
</div>
</ng-template>
<nb-icon *ngIf="participant.memo" [nbPopover]="memoBox" nbPopoverTrigger="hover"
style="width: 0.75em; height: 0.75em;" icon="message-circle-outline">
<nb-icon *ngIf="participant.memo" [nbPopover]="memoBox"
nbPopoverTrigger="hover" style="width: 0.75em; height: 0.75em;"
icon="message-circle-outline">
</nb-icon>
<span *ngIf="participant.rank=='Trial'" style="font-size: 9px;">
Trial
@@ -244,7 +257,6 @@
<tr>
<td>
<wowhead [item]="item.value[0]"></wowhead><br />
</td>
</tr>
<tr>
@@ -254,7 +266,15 @@
<span
[ngStyle]="{'text-decoration': token.rank=='Trial'? 'line-through' : 'none currentcolor solid' }">
<span style="text-transform: capitalize;">
<button (click)="decreaseReserve(token)" *ngIf="changeReserve" nbButton
outline status="warning" size="tiny">
-
</button>
[ {{token.level}} ]&nbsp;
<button (click)="increaseReserve(token)" *ngIf="changeReserve" nbButton
outline status="warning" size="tiny">
+
</button>
<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'}">
{{token.charactername}}
@@ -26,6 +26,7 @@ export class FrontcraftRaidComponent
isTier = false
reservesShown = true
manageRaid
changeReserve
mySignup: (Signup & Character & Spec)
raid: RaidData = <any>{
@@ -64,6 +65,7 @@ export class FrontcraftRaidComponent
ngOnInit() {
this.manageRaid = this.api.get('manageRaid')
this.changeReserve = this.api.get('changeReserve')
const signupFeature = this.api.get('signup')
if (signupFeature) {
@@ -187,7 +189,6 @@ export class FrontcraftRaidComponent
}
display = async (raiddata: RaidData) => {
this.logger.log(raiddata)
this.isTier = allItems[raiddata.tier] != null
this.raid = raiddata
@@ -248,4 +249,31 @@ export class FrontcraftRaidComponent
.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{
const sock = new RPCSocket(
auth.port,
window.location.hostname
window.location.hostname,
)
sock.hook('kick', () => {
@@ -48,7 +48,7 @@ export class ClientApiService implements IApiService{
sock.on('error', async (e) => {
this.logger.log("priv#error", e);
await this.logout()
//handled via unprivileged socket
})
const authSock = await sock.connect(auth.token.value)
@@ -95,7 +95,6 @@ export class ClientApiService implements IApiService{
throw new Error("Login failed")
}
//attach error handler now so failed logins dont trigger reloads
this.socket.on('error', async (err) => {
location.reload()
})