work
This commit is contained in:
@@ -11,7 +11,7 @@ import { GuildManager } from '../Components/Guild/GuildManager';
|
||||
import { ItemManager } from '../Components/Item/ItemManager';
|
||||
import { RaidManager } from '../Components/Raid/RaidManager';
|
||||
import { CharacterManager } from '../Components/Character/CharacterManager';
|
||||
import { LoginManager } from '../Components/Login/LoginManager';
|
||||
import { UserManager } from '../Components/User/UserManager';
|
||||
import { RootComponent } from '../Injector/ServiceDecorator';
|
||||
import { TableDefinitionExporter } from '../Types/Interfaces';
|
||||
import { AdminConf, TableDefiniton } from '../Types/Types';
|
||||
@@ -19,18 +19,20 @@ import { RPCConfigLoader } from '../Components/RPCConfigLoader';
|
||||
import { FrontworkComponent } from '../Types/FrontworkComponent';
|
||||
import { IAdmin } from './Interface';
|
||||
import { Injector } from '../Injector/Injector';
|
||||
import { Shoutbox } from '../Components/Shoutbox/Shoutbox';
|
||||
|
||||
|
||||
const logger = getLogger("admin", 'debug')
|
||||
|
||||
@RootComponent({
|
||||
implements: IAdmin,
|
||||
imports: [
|
||||
injectable: IAdmin,
|
||||
injects: [
|
||||
GuildManager,
|
||||
ItemManager,
|
||||
RaidManager,
|
||||
CharacterManager,
|
||||
LoginManager
|
||||
UserManager,
|
||||
Shoutbox
|
||||
]
|
||||
})
|
||||
export class FrontworkAdmin
|
||||
@@ -169,7 +171,11 @@ implements TableDefinitionExporter, IAdmin {
|
||||
}
|
||||
|
||||
this.knex = Knex(conf)
|
||||
|
||||
if(conf.client === 'sqlite3'){
|
||||
await this.knex.raw('PRAGMA foreign_keys = ON');
|
||||
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
this.getTableDefinitions()
|
||||
//make unique by name
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { RPCConfigLoader } from "../Components/RPCConfigLoader"
|
||||
import { AdminConf } from "../Types/Types"
|
||||
import { RPCServer } from "rpclibrary"
|
||||
import Knex = require("knex")
|
||||
import * as Knex from "knex"
|
||||
|
||||
export class IAdmin{
|
||||
knex: Knex
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { RPCInterface } from "rpclibrary";
|
||||
import { Inject, Module } from "../../Injector/ServiceDecorator";
|
||||
import { TableDefiniton, Character, Spec, User } from "../../Types/Types";
|
||||
import { Inject, Injectable } from "../../Injector/ServiceDecorator";
|
||||
import { TableDefiniton, Character, Spec, User, Class, _Rank, Rank } from "../../Types/Types";
|
||||
import { CharacterManagerIfc } from "./RPCInterface";
|
||||
import { FrontworkComponent } from "../../Types/FrontworkComponent";
|
||||
import { getSpecTableData, SpecT } from "../../Types/PlayerSpecs";
|
||||
import { IAdmin } from "../../Admin/Interface";
|
||||
import { ILoginManager } from "../Login/Interface";
|
||||
import { IUserManager } from "../User/Interface";
|
||||
import { ICharacterManager } from "./Interface";
|
||||
import { getLogger } from "log4js";
|
||||
|
||||
@Module(ICharacterManager)
|
||||
@Injectable(ICharacterManager)
|
||||
export class CharacterManager
|
||||
implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterManager{
|
||||
name = "CharacterManager" as "CharacterManager";
|
||||
@@ -17,35 +16,20 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
|
||||
@Inject(IAdmin)
|
||||
private admin: IAdmin
|
||||
|
||||
@Inject(ILoginManager)
|
||||
private loginManager : ILoginManager
|
||||
@Inject(IUserManager)
|
||||
private loginManager : IUserManager
|
||||
|
||||
exportRPCs = () => [
|
||||
{
|
||||
name: 'getSpecId' as 'getSpecId',
|
||||
call: this.getSpecId
|
||||
},{
|
||||
name: 'getCharacterByName' as 'getCharacterByName',
|
||||
call: this.getCharacterByName
|
||||
},{
|
||||
name: 'getCharacters' as 'getCharacters',
|
||||
call: this.getCharacters
|
||||
},{
|
||||
name: 'getCharactersOfUser' as 'getCharactersOfUser',
|
||||
call: this.getCharactersOfUser
|
||||
}
|
||||
this.getSpecId,
|
||||
this.getCharacterByName,
|
||||
this.getCharacters,
|
||||
this.getCharactersOfUser,
|
||||
this.createCharacter,
|
||||
this.getUserOfCharacter,
|
||||
this.getHeadCount
|
||||
]
|
||||
|
||||
exportRPCFeatures = () => [
|
||||
{
|
||||
name: "createCharacter",
|
||||
exportRPCs: () => [
|
||||
{
|
||||
name: 'createCharacter',
|
||||
call: this.createCharacter
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
getTableDefinitions = (): TableDefiniton[] => [
|
||||
@@ -58,6 +42,8 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
|
||||
table.foreign("specid").references("specs.id")
|
||||
table.integer("userid").notNullable()
|
||||
table.foreign("userid").references("users.id")
|
||||
table.string("race").notNullable()
|
||||
table.boolean('alt').defaultTo(false)
|
||||
}
|
||||
},{
|
||||
name: 'specs',
|
||||
@@ -87,13 +73,16 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
|
||||
return char
|
||||
}catch(e){
|
||||
console.log(e);
|
||||
|
||||
}
|
||||
throw new Error('Unable to create character')
|
||||
}
|
||||
|
||||
getCharacters = async() : Promise<Character[]> => {
|
||||
return this.admin.knex.select('*').from('characters')
|
||||
getCharacters = async() : Promise<(Character & Spec & User)[]> => {
|
||||
return this.admin.knex
|
||||
.select('*')
|
||||
.from('characters as c')
|
||||
.join('specs as sp', 'c.specid', 'sp.id')
|
||||
.join('users as u', 'c.userid', 'u.id')
|
||||
}
|
||||
|
||||
getCharacterByName = async(charactername: string) : Promise<(Character & User & Spec) | void> => {
|
||||
@@ -101,7 +90,7 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
|
||||
return await this.admin.knex('characters as c')
|
||||
.join('specs as s', 's.id', '=', 'c.specid')
|
||||
.join('users as u', 'u.id', '=', 'c.userid')
|
||||
.select('charactername', 'class', 'specname', 'username', 'rank', 'locked', )
|
||||
.select('c.id as id', 'charactername', 'race', 'specid', 'class', 'specname', 'username', 'rank')
|
||||
.where('charactername', '=', charactername)
|
||||
.first()
|
||||
}
|
||||
@@ -111,10 +100,16 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
|
||||
return await this.admin.knex('characters as c')
|
||||
.join('users as u', 'u.id', '=', 'c.userid')
|
||||
.join('specs as s', 's.id', '=', 'c.specid')
|
||||
.select('class', 'charactername', 'class', 'specname')
|
||||
.select('class', 'charactername', 'class', 'race', 'specname', 'userid', 'c.id as id', 'specid')
|
||||
.where('u.username', '=', username)
|
||||
}
|
||||
|
||||
getUserOfCharacter = async(character: Character) : Promise<User> => {
|
||||
return await this.admin.knex('users').where({
|
||||
id: character.userid
|
||||
}).first()
|
||||
}
|
||||
|
||||
getSpecId = async <c extends keyof SpecT>(clazz: c, name: SpecT[c]) => await this.admin.knex
|
||||
.from('specs')
|
||||
.select('id')
|
||||
@@ -125,4 +120,20 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
|
||||
.first()
|
||||
.then(spec => spec.id)
|
||||
|
||||
getHeadCount = async(c:Class):Promise<number> => {
|
||||
const cnt = await this.admin
|
||||
.knex('characters as c')
|
||||
.join('users as u', 'c.userid', '=', 'u.id')
|
||||
.join('specs as s', 'c.specid', '=', 's.id')
|
||||
.where({
|
||||
class: c
|
||||
}).andWhere(function(){
|
||||
this.whereNotIn('u.rank', <Rank[]>[
|
||||
'Guest'
|
||||
])
|
||||
this.whereNot('c.alt', '1')
|
||||
})
|
||||
.count('*')
|
||||
return cnt[0]['count(*)'] as number
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Character, Spec, User } from "../../Types/Types"
|
||||
import { Character, Spec, User, Class } from "../../Types/Types"
|
||||
import { SpecT } from "../../Types/PlayerSpecs"
|
||||
|
||||
|
||||
export class ICharacterManager{
|
||||
createCharacter: (usertoken: string, char : Character) => Promise<Character>
|
||||
getSpecId: <c extends keyof SpecT>(clazz: c, name: SpecT[c]) => Promise<number>
|
||||
getCharacters: () => Promise<Character[]>
|
||||
getCharacters: () => Promise<(Character & Spec & User)[]>
|
||||
getCharacterByName: (charactername: string) => Promise<(Character & User & Spec) | void>
|
||||
getCharactersOfUser: (username: string) => Promise<(Character & Spec)[]>
|
||||
getUserOfCharacter: (character:Character) => Promise<User>
|
||||
getHeadCount: (clazz: Class) => Promise<number>
|
||||
}
|
||||
@@ -7,11 +7,12 @@ export type CharacterManagerIfc = {
|
||||
getCharacters : ICharacterManager['getCharacters']
|
||||
getCharacterByName : ICharacterManager['getCharacterByName']
|
||||
getCharactersOfUser: ICharacterManager['getCharactersOfUser']
|
||||
createCharacter: ICharacterManager['createCharacter']
|
||||
getUserOfCharacter: ICharacterManager['getUserOfCharacter']
|
||||
getHeadCount: ICharacterManager['getHeadCount']
|
||||
}
|
||||
}
|
||||
|
||||
export type CharacterManagerFeatureIfc = {
|
||||
createCharacter: {
|
||||
createCharacter: ICharacterManager['createCharacter']
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { RPCExporter } from "rpclibrary"
|
||||
import { FrontworkAdmin } from "../../Admin/Admin"
|
||||
import { TableDefiniton } from "../../Types/Types"
|
||||
import { PrivilegedRPCExporter } from "../../Types/PrivilegedRPCExporter"
|
||||
import { FrontworkComponent } from "../../Types/FrontworkComponent"
|
||||
|
||||
export class Debugger
|
||||
implements FrontworkComponent<any,any>{
|
||||
public admin: FrontworkAdmin
|
||||
name = "Debugger"
|
||||
|
||||
|
||||
constructor(private exporters: PrivilegedRPCExporter[]){
|
||||
}
|
||||
|
||||
exportRPCs(){
|
||||
|
||||
return [{
|
||||
name: 'getTable',
|
||||
call: async(table:string) => this.admin.knex.select('*').from(table)
|
||||
},
|
||||
...this.exporters.flatMap(e => e.exportRPCs()),
|
||||
...this.exporters.flatMap(e => e.exportRPCFeatures().flatMap(e => e.exportRPCs()))]
|
||||
}
|
||||
|
||||
exportRPCFeatures(): RPCExporter<any, any, {}>[] {
|
||||
return []
|
||||
}
|
||||
|
||||
getTableDefinitions(): TableDefiniton[] {
|
||||
return []
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ConfigLoader } from "loadson";
|
||||
import { Inject, Module } from "../../Injector/ServiceDecorator";
|
||||
import { Inject, Injectable } from "../../Injector/ServiceDecorator";
|
||||
import { GuildManagerFeatureIfc, GuildManagerIfc } from "./RPCInterface";
|
||||
import { FrontworkComponent } from "../../Types/FrontworkComponent";
|
||||
import { _Rank, Rank } from "../../Types/Types";
|
||||
@@ -10,9 +10,9 @@ export type Guild = {
|
||||
name: string
|
||||
realm: string
|
||||
description: string
|
||||
}
|
||||
};
|
||||
|
||||
@Module(IGuildManager)
|
||||
@Injectable(IGuildManager)
|
||||
export class GuildManager
|
||||
implements FrontworkComponent<GuildManagerIfc, GuildManagerFeatureIfc>, IGuildManager{
|
||||
|
||||
@@ -32,26 +32,18 @@ implements FrontworkComponent<GuildManagerIfc, GuildManagerFeatureIfc>, IGuildMa
|
||||
}
|
||||
}, "./config")
|
||||
|
||||
exportRPCs = () => [{
|
||||
name: 'getHeadCount' as 'getHeadCount',
|
||||
call: this.getHeadCount
|
||||
},{
|
||||
name: 'getGuildInfo' as 'getGuildInfo',
|
||||
call: this.getGuildInfo
|
||||
}]
|
||||
exportRPCs = () => [
|
||||
this.getHeadCount,
|
||||
this.getGuildInfo
|
||||
]
|
||||
|
||||
exportRPCFeatures = () => [{
|
||||
name: 'manageGuild' as 'manageGuild',
|
||||
exportRPCs: () => [{
|
||||
name: 'setName' as 'setName',
|
||||
call: this.setName
|
||||
},{
|
||||
name: 'setRealm' as 'setRealm',
|
||||
call: this.setRealm
|
||||
}, {
|
||||
name: 'setDescription' as 'setDescription',
|
||||
call: this.setDescription
|
||||
}]
|
||||
exportRPCs: () => [
|
||||
this.setName,
|
||||
this.setRealm,
|
||||
this.setDescription
|
||||
]
|
||||
}]
|
||||
|
||||
getTableDefinitions = () => []
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { Item } from "../../Types/Types"
|
||||
import { Item, Character, SRToken, SRPriority, Spec } from "../../Types/Types"
|
||||
|
||||
export class IItemManager{
|
||||
getItems: () => Promise<Item[]>
|
||||
getItem: (name:string) => Promise<Item>
|
||||
fetchItem: (name:string) => Promise<Item>
|
||||
buyToken: (usertoken: string, charactername:string, itemname:string) => Promise<(SRToken & Character & Item) | void>
|
||||
setPriority: (itemname:string, priority: any) => Promise<void>
|
||||
calculatePriorities: (itemname: string, character:Character) => Promise<number>
|
||||
deletePriority: (priority:SRPriority) => Promise<void>
|
||||
getTokens: (character:Character) => Promise<SRToken[]>
|
||||
getToken: (character:Character, item:Item) => Promise<(SRToken & Character & Item) | void>
|
||||
getAllPriorities: () => Promise<(SRPriority & Spec & Item)[]>
|
||||
wipeCurrencyAndItems: () => Promise<void>
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
import { T1 } from "../../Types/Items";
|
||||
import { RPC } from "rpclibrary";
|
||||
import { Inject, Module } from "../../Injector/ServiceDecorator";
|
||||
import { Inject, Injectable } from "../../Injector/ServiceDecorator";
|
||||
import { ItemManagerFeatureIfc, ItemManagerIfc } from "./RPCInterface";
|
||||
import { FrontworkComponent } from "../../Types/FrontworkComponent";
|
||||
import { TableDefinitionExporter } from "../../Types/Interfaces";
|
||||
import { TableDefiniton, Item } from "../../Types/Types";
|
||||
import { TableDefiniton, Item, User, Character, SRToken, SRPriority, Spec } from "../../Types/Types";
|
||||
import { IAdmin } from "../../Admin/Interface";
|
||||
import { IItemManager } from "./Interface";
|
||||
import { getLogger } from "log4js";
|
||||
import { IUserManager } from "../User/Interface";
|
||||
import { ICharacterManager } from "../Character/Interface";
|
||||
import { join } from "path";
|
||||
|
||||
const fetch = require('node-fetch')
|
||||
const xml2js = require('xml2js');
|
||||
const parser = new xml2js.Parser(/* options */);
|
||||
|
||||
@Module(IItemManager)
|
||||
@Injectable(IItemManager)
|
||||
export class ItemManager
|
||||
implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefinitionExporter, IItemManager{
|
||||
name = "ItemManager" as "ItemManager";
|
||||
@@ -21,23 +23,49 @@ implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefin
|
||||
@Inject(IAdmin)
|
||||
private admin: IAdmin
|
||||
|
||||
exportRPCs(): RPC<any, any>[]{
|
||||
return [{
|
||||
name: 'getItems',
|
||||
call: this.getItems
|
||||
},{
|
||||
name: 'getItem',
|
||||
call: this.getItem
|
||||
}]
|
||||
}
|
||||
@Inject(IUserManager)
|
||||
private userManager: IUserManager
|
||||
|
||||
exportRPCFeatures() {
|
||||
return []
|
||||
@Inject(ICharacterManager)
|
||||
private character: ICharacterManager
|
||||
|
||||
exportRPCs = () => [
|
||||
this.getItems,
|
||||
this.getItem,
|
||||
this.buyToken,
|
||||
this.calculatePriorities,
|
||||
this.getToken,
|
||||
this.getTokens,
|
||||
this.getAllPriorities
|
||||
]
|
||||
|
||||
exportRPCFeatures = () => [{
|
||||
name: 'managePriorities' as 'managePriorities',
|
||||
exportRPCs: () => [
|
||||
this.setPriority,
|
||||
this.deletePriority
|
||||
],
|
||||
},{
|
||||
name: 'reset' as 'reset',
|
||||
exportRPCs: () => [
|
||||
this.wipeCurrencyAndItems
|
||||
]
|
||||
}]
|
||||
|
||||
wipeCurrencyAndItems = async () => {
|
||||
await Promise.all([
|
||||
this.userManager.wipeCurrency(),
|
||||
this.admin.knex('tokens').where(true).del()
|
||||
])
|
||||
}
|
||||
|
||||
getItems = async () :Promise<Item[]> => await this.admin.knex.select('*').from('items')
|
||||
|
||||
getItem = async (name:string):Promise<Item> => {
|
||||
getItem = async(name:string):Promise<Item> => {
|
||||
return await this.admin.knex('items').select('*').where('itemname','=',name).first()
|
||||
}
|
||||
|
||||
fetchItem = async (name:string):Promise<Item> => {
|
||||
const res = await fetch('https://classic.wowhead.com/item='+name+'&xml');
|
||||
const txt = await res.text();
|
||||
const r = await parser.parseStringPromise(txt);
|
||||
@@ -60,11 +88,24 @@ implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefin
|
||||
{
|
||||
name: 'tokens',
|
||||
tableBuilder: (table) => {
|
||||
table.integer("characterid").primary()
|
||||
table.primary(['characterid', 'itemid'])
|
||||
table.integer("characterid")
|
||||
table.foreign("characterid").references("id").inTable('characters')
|
||||
table.integer("itemid").primary()
|
||||
table.integer("itemid")
|
||||
table.foreign("itemid").references("id").inTable('items')
|
||||
table.integer("level")
|
||||
table.integer("level").defaultTo(1)
|
||||
}
|
||||
},{
|
||||
name: 'priorities',
|
||||
tableBuilder: (table) => {
|
||||
table.increments('id').primary()
|
||||
table.integer('itemid')
|
||||
table.foreign('itemid').references('id').inTable('items')
|
||||
table.string('race').nullable()
|
||||
table.integer('specid').nullable()
|
||||
table.foreign('specid').references('id').inTable('specs')
|
||||
table.integer('modifier')
|
||||
table.string('description').nullable()
|
||||
}
|
||||
},{
|
||||
name: 'items',
|
||||
@@ -79,6 +120,126 @@ implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefin
|
||||
}]
|
||||
}
|
||||
|
||||
buyToken = async (usertoken: string, charactername:string, itemname:string): Promise<(SRToken & Character & Item) | void> => {
|
||||
const record = this.userManager.getUserRecordByToken(usertoken)
|
||||
const character = await this.character.getCharacterByName(charactername)
|
||||
|
||||
if(!record || !character || record.user.username !== character.username) return
|
||||
|
||||
const item = await this.getItem(itemname)
|
||||
if(!item) return
|
||||
|
||||
const currency = await this.userManager.getCurrency(record.user)
|
||||
if(currency < 1) return
|
||||
|
||||
const existingToken = await this.getToken(character, item)
|
||||
|
||||
await this.userManager.decrementCurrency(record.user, 1)
|
||||
|
||||
if(!existingToken){
|
||||
const modifier = await this.calculatePriorities(itemname, character)
|
||||
await this.admin.knex('tokens').insert({
|
||||
characterid: character.id,
|
||||
itemid: item.id,
|
||||
level: 1+modifier
|
||||
})
|
||||
}else{
|
||||
await this.admin.knex('tokens').where({
|
||||
characterid: character.id,
|
||||
itemid: item.id
|
||||
}).increment('level')
|
||||
}
|
||||
|
||||
return await this.getToken(character, item)
|
||||
}
|
||||
|
||||
getAllPriorities = async() :Promise<(SRPriority & Spec & Item)[]> => this.admin
|
||||
.knex('priorities as p')
|
||||
.join('items as i', 'p.itemid', '=', 'i.id')
|
||||
.leftJoin('specs as s', 'p.specid', '=', 's.id')
|
||||
.select('*')
|
||||
|
||||
deletePriority = async(priority:SRPriority) : Promise<void> => {
|
||||
await this.admin.knex('priorities')
|
||||
.where(priority)
|
||||
.del()
|
||||
}
|
||||
|
||||
setPriority = async(itemname:string, priority: SRPriority) : Promise<void> => {
|
||||
const item = await this.getItem(itemname)
|
||||
|
||||
await this.admin
|
||||
.knex('priorities')
|
||||
.insert(<SRPriority>{
|
||||
itemid: item.id,
|
||||
...priority
|
||||
})
|
||||
}
|
||||
|
||||
getPriorities = async(itemname:string) : Promise<SRPriority[]> => {
|
||||
const item = await this.getItem(itemname)
|
||||
|
||||
return await this.admin.knex('priorities')
|
||||
.where('itemid', '=', item.id)
|
||||
.select('*')
|
||||
}
|
||||
|
||||
calculatePriorities = async (itemname: string, character:Character):Promise<number>=> {
|
||||
const rules : SRPriority[] = await this.admin.knex('priorities as p')
|
||||
.select('*')
|
||||
.join('items as i', 'i.id', '=', 'p.itemid')
|
||||
.where('itemname', '=', itemname)
|
||||
|
||||
return rules.map(rule => {
|
||||
if(rule.specid && rule.race){
|
||||
if(rule.specid === character.specid && rule.race === character.race)
|
||||
return rule.modifier
|
||||
else
|
||||
return 0
|
||||
}
|
||||
|
||||
if(rule.specid){
|
||||
if(rule.specid === character.specid)
|
||||
return rule.modifier
|
||||
else
|
||||
return 0
|
||||
}
|
||||
|
||||
if(rule.race){
|
||||
if(rule.race === character.race)
|
||||
return rule.modifier
|
||||
else
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
}).reduce((prev, curr) => prev+curr, 0)
|
||||
}
|
||||
|
||||
getToken = async (character:Character, item:Item): Promise<(SRToken & Character & Item) | void>=> {
|
||||
return await this.admin
|
||||
.knex('tokens as t')
|
||||
.select('*')
|
||||
.join('characters as c', 'c.id', '=', 't.characterid')
|
||||
.join('items as i', 'i.id', '=', 't.itemid')
|
||||
.where({
|
||||
characterid: character.id,
|
||||
itemid: item.id
|
||||
})
|
||||
.first()
|
||||
}
|
||||
|
||||
getTokens = async (character:Character) : Promise<(SRToken & Character & Item)[]> => {
|
||||
return await this.admin
|
||||
.knex('tokens as t')
|
||||
.select('*')
|
||||
.select('*')
|
||||
.join('characters as c', 'c.id', '=', 't.characterid')
|
||||
.join('items as i', 'i.id', '=', 't.itemid')
|
||||
.where({
|
||||
characterid: character.id,
|
||||
})
|
||||
}
|
||||
|
||||
countItems = async() :Promise<number> => {
|
||||
const count = await this.admin.knex('items').count('*');
|
||||
return <number>count[0]['count(*)']
|
||||
@@ -86,15 +247,15 @@ implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefin
|
||||
|
||||
private initialized = false
|
||||
initialize = async () => {
|
||||
if(!this.initialized)
|
||||
this.initialized = true
|
||||
if(this.initialized) return
|
||||
this.initialized = true
|
||||
|
||||
const allItems = [...T1]
|
||||
getLogger('ItemManager').debug('Checking items')
|
||||
|
||||
const countCache = await this.countItems()
|
||||
if(countCache != allItems.length){
|
||||
const items:Item[] = await Promise.all(allItems.map((i) => this.getItem(i)))
|
||||
const items:Item[] = await Promise.all(allItems.map((i) => this.fetchItem(i)))
|
||||
try{
|
||||
await this.admin
|
||||
.knex('items')
|
||||
|
||||
@@ -2,10 +2,22 @@ import { IItemManager } from "./Interface"
|
||||
|
||||
export type ItemManagerIfc = {
|
||||
ItemManager: {
|
||||
getItem: IItemManager['getItem']
|
||||
getItem: IItemManager['fetchItem']
|
||||
getItems: IItemManager['getItems']
|
||||
buyToken: IItemManager['buyToken']
|
||||
calculatePriorities: IItemManager['calculatePriorities']
|
||||
getToken: IItemManager['getToken']
|
||||
getTokens: IItemManager['getTokens']
|
||||
getAllPriorities: IItemManager['getAllPriorities']
|
||||
}
|
||||
}
|
||||
|
||||
export type ItemManagerFeatureIfc = {
|
||||
reset: {
|
||||
wipeCurrencyAndItems: IItemManager['wipeCurrencyAndItems']
|
||||
}
|
||||
managePriorities: {
|
||||
setPriority:IItemManager['setPriority']
|
||||
deletePriorits: IItemManager['deletePriority']
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { ILoginManager } from "./Interface"
|
||||
|
||||
export type LoginManagerIfc = {
|
||||
Authenticator: {
|
||||
checkToken: ILoginManager['checkToken']
|
||||
login: ILoginManager['login']
|
||||
logout: ILoginManager['logout']
|
||||
createUser: ILoginManager['createUser']
|
||||
getAuth: ILoginManager['getAuth']
|
||||
}
|
||||
}
|
||||
|
||||
export type LoginManagerFeatureIfc = {
|
||||
modifyPermissions: {
|
||||
setPermission: ILoginManager['setPermission']
|
||||
getPermissions: ILoginManager['getPermissions']
|
||||
}
|
||||
}
|
||||
@@ -18,25 +18,12 @@ implements RPCExporter<ConfigLoaderIfc<ConfT>, "Config">{
|
||||
|
||||
name = "Config" as "Config"
|
||||
|
||||
exportRPCs() {
|
||||
return [{
|
||||
name: "getConfig" as "getConfig",
|
||||
call: this.getConfig
|
||||
},{
|
||||
name: "resetConfig" as "resetConfig",
|
||||
call: this.resetConfig
|
||||
},{
|
||||
name: "setConfig" as "setConfig",
|
||||
call: this.setConfig
|
||||
},{
|
||||
name: "setConfigKey" as "setConfigKey",
|
||||
call: this.setConfigKey
|
||||
},{
|
||||
name: "deleteConfigKey" as "deleteConfigKey",
|
||||
call: this.deleteConfigKey
|
||||
},{
|
||||
name: "getConfigKey" as "getConfigKey",
|
||||
call: this.getConfigKey
|
||||
}]
|
||||
}
|
||||
exportRPCs = () => [
|
||||
this.getConfig,
|
||||
this.resetConfig,
|
||||
this.setConfig,
|
||||
this.setConfigKey,
|
||||
this.deleteConfigKey,
|
||||
this.getConfigKey
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Raid, Signup, Character } from "../../Types/Types"
|
||||
import { Raid, Signup, Character, RaidData } from "../../Types/Types"
|
||||
|
||||
export class IRaidManager{
|
||||
getRaids: () => Promise<Raid[]>
|
||||
@@ -6,5 +6,12 @@ export class IRaidManager{
|
||||
addSignup: (signup: Signup) => Promise<any>
|
||||
removeSignup: (signup: Signup) => Promise<any>
|
||||
getSignups: (raid:Raid) => Promise<Signup[]>
|
||||
sign: (userToken: string, character:Character, raid:Raid, attending:boolean) => Promise<any>
|
||||
sign: (userToken: string, character:Character, raid:Raid, late:boolean) => Promise<any>
|
||||
unsign: (userToken: string, character:Character, raid:Raid,) => Promise<any>
|
||||
archiveRaid: (raid:Raid) => Promise<RaidData>
|
||||
getRaidData: (raid:Raid) => Promise<RaidData>
|
||||
setBenched: (signup: Signup) => Promise<void>
|
||||
getPastRaids: (limit: number) => Promise<RaidData[]>
|
||||
getArchiveRaid: (id:number) => Promise<RaidData>
|
||||
startRaid: (raid:Raid) => Promise<RaidData>
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ import { IRaidManager } from "./Interface"
|
||||
export type RaidManagerIfc = {
|
||||
RaidManager:{
|
||||
getRaids: IRaidManager['getRaids']
|
||||
getRaidData: IRaidManager['getRaidData']
|
||||
getPastRaids: IRaidManager['getPastRaids']
|
||||
getArchiveRaid: IRaidManager['getArchiveRaid']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +14,13 @@ export type RaidManagerFeatureIfc = {
|
||||
createRaid: IRaidManager['createRaid']
|
||||
addSignup: IRaidManager['addSignup']
|
||||
removeSignup: IRaidManager['removeSignup']
|
||||
archiveRaid: IRaidManager['archiveRaid']
|
||||
setBenched: IRaidManager['setBenched']
|
||||
startRaid: IRaidManager['startRaid']
|
||||
}
|
||||
signup: {
|
||||
getSignups: IRaidManager['getSignups']
|
||||
sign: IRaidManager['sign']
|
||||
unsign: IRaidManager['unsign']
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Inject, Module } from "../../Injector/ServiceDecorator";
|
||||
import { Inject, Injectable } from "../../Injector/ServiceDecorator";
|
||||
import { RaidManagerIfc, RaidManagerFeatureIfc } from "./RPCInterface";
|
||||
import { FrontworkComponent } from "../../Types/FrontworkComponent";
|
||||
import { TableDefiniton, Signup, Raid, User, Character } from "../../Types/Types";
|
||||
import { TableDefiniton, Signup, Raid, Character, RaidData, Spec, SRToken, Item } from "../../Types/Types";
|
||||
import { IAdmin } from "../../Admin/Interface";
|
||||
import { IRaidManager } from "./Interface";
|
||||
import { ILoginManager } from "../Login/Interface";
|
||||
import { IUserManager } from "../User/Interface";
|
||||
import { ICharacterManager } from "../Character/Interface";
|
||||
|
||||
@Module(IRaidManager)
|
||||
@Injectable(IRaidManager)
|
||||
export class RaidManager
|
||||
implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManager{
|
||||
name = "RaidManager" as "RaidManager";
|
||||
@@ -14,37 +15,38 @@ implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManag
|
||||
@Inject(IAdmin)
|
||||
private admin: IAdmin
|
||||
|
||||
@Inject(ILoginManager)
|
||||
private login: ILoginManager
|
||||
@Inject(IUserManager)
|
||||
private userManager: IUserManager
|
||||
|
||||
exportRPCs = () => [{
|
||||
name: 'getRaids' as 'getRaids',
|
||||
call: this.getRaids
|
||||
},]
|
||||
@Inject(ICharacterManager)
|
||||
private characterManager: ICharacterManager
|
||||
|
||||
exportRPCs = () => [
|
||||
this.getRaids,
|
||||
this.getRaidData,
|
||||
this.getPastRaids,
|
||||
this.getArchiveRaid
|
||||
]
|
||||
|
||||
exportRPCFeatures() {
|
||||
return [{
|
||||
name: 'manageRaid' as 'manageRaid',
|
||||
exportRPCs: () => [{
|
||||
name: 'createRaid' as 'createRaid',
|
||||
call: this.createRaid
|
||||
},{
|
||||
name: 'addSignup' as 'addSignup',
|
||||
call: this.addSignup
|
||||
},{
|
||||
name: 'removeSignup' as 'removeSignup',
|
||||
call: this.removeSignup
|
||||
}]
|
||||
exportRPCs: () => [
|
||||
this.createRaid,
|
||||
this.addSignup,
|
||||
this.removeSignup,
|
||||
this.archiveRaid,
|
||||
this.setBenched,
|
||||
this.startRaid
|
||||
]
|
||||
},{
|
||||
name: 'signup' as 'signup',
|
||||
exportRPCs: () => [{
|
||||
name: 'getSignups' as 'getSignups',
|
||||
call: this.getSignups
|
||||
},{
|
||||
name: 'sign' as 'sign',
|
||||
call: this.sign
|
||||
}]
|
||||
},]
|
||||
exportRPCs: () => [
|
||||
this.getSignups,
|
||||
this.sign,
|
||||
this.unsign
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
getTableDefinitions(): TableDefiniton[] {
|
||||
@@ -56,16 +58,24 @@ implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManag
|
||||
table.dateTime('start').notNullable()
|
||||
table.string('description').notNullable()
|
||||
table.string('title').notNullable()
|
||||
table.integer('minrank').notNullable()
|
||||
table.integer('size').defaultTo(40)
|
||||
}
|
||||
},{
|
||||
name: 'archive',
|
||||
tableBuilder: (table) => {
|
||||
table.integer('id').primary()
|
||||
table.json('raiddata').notNullable()
|
||||
}
|
||||
},{
|
||||
name: 'signups',
|
||||
tableBuilder: (table) => {
|
||||
table.primary(['raidid', 'characterid'])
|
||||
table.integer('raidid')
|
||||
table.foreign('raidid').references('id').inTable('raids')
|
||||
table.foreign('raidid').references('id').inTable('raids').onDelete('CASCADE')
|
||||
table.integer('characterid')
|
||||
table.foreign('characterid').references('id').inTable('characters')
|
||||
table.foreign('characterid').references('id').inTable('characters').onDelete('CASCADE')
|
||||
table.boolean('benched').defaultTo('false')
|
||||
table.boolean('late')
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -87,9 +97,154 @@ implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManag
|
||||
})
|
||||
.delete()
|
||||
|
||||
getRaids = async () : Promise<Raid[]> => await this.admin.knex
|
||||
.select('*')
|
||||
.from('raids')
|
||||
getRaids = async () : Promise<Raid[]> => {
|
||||
|
||||
const subQuery = this.admin
|
||||
.knex('signups')
|
||||
.count('*')
|
||||
.where({
|
||||
raidid: this.admin.knex.ref('raids.id'),
|
||||
benched: false,
|
||||
late: false
|
||||
})
|
||||
.as('signupcount')
|
||||
|
||||
return await this.admin.knex('raids')
|
||||
.select('*', subQuery)
|
||||
.orderBy('start', 'asc')
|
||||
}
|
||||
|
||||
startRaid = async (raid:Raid) : Promise<RaidData> => {
|
||||
const archived = await this.archiveRaid(raid)
|
||||
|
||||
delete archived.participants.late
|
||||
|
||||
const giveCurrency = async (b: Character) => {
|
||||
const usr = await this.characterManager.getUserOfCharacter(b)
|
||||
await this.userManager.incrementCurrency(usr, 1)
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
...archived.participants.bench.map(giveCurrency),
|
||||
...Object.values(archived.participants).map((group: any) => group.map(giveCurrency))
|
||||
])
|
||||
|
||||
return archived
|
||||
}
|
||||
|
||||
archiveRaid = async (raid:Raid) : Promise<RaidData> => {
|
||||
const raidData = await this.getRaidData(raid)
|
||||
await this.admin.knex('archive')
|
||||
.insert({
|
||||
id:raidData.id,
|
||||
raiddata: JSON.stringify(raidData)
|
||||
})
|
||||
|
||||
|
||||
await this.admin.knex('raids')
|
||||
.where('id', '=', raid.id)
|
||||
.del()
|
||||
|
||||
const row = await this.admin.knex('archive')
|
||||
.select('*')
|
||||
.where({
|
||||
id:raidData.id,
|
||||
})
|
||||
.first()
|
||||
|
||||
return JSON.parse(row.raiddata)
|
||||
}
|
||||
|
||||
getArchiveRaid = async(id:number) : Promise<RaidData> => {
|
||||
const data = await this.admin.knex('archive').select('raiddata').where({
|
||||
id: id
|
||||
}).first()
|
||||
|
||||
return JSON.parse(data.raiddata)
|
||||
}
|
||||
|
||||
getPastRaids = async(limit: number) : Promise<RaidData[]> => {
|
||||
const raids = await this.admin.knex('archive')
|
||||
.select('*')
|
||||
.orderBy('id', 'desc')
|
||||
.limit(limit)
|
||||
|
||||
return raids.map(raid => JSON.parse(raid.raiddata))
|
||||
}
|
||||
|
||||
getRaidData = async (raid:Raid) : Promise<RaidData> => {
|
||||
const ret = {
|
||||
participants:{
|
||||
Druid: <(Character&Spec)[]>[],
|
||||
Hunter: <(Character&Spec)[]>[],
|
||||
Mage: <(Character&Spec)[]>[],
|
||||
Paladin: <(Character&Spec)[]>[],
|
||||
Priest: <(Character&Spec)[]>[],
|
||||
Rogue: <(Character&Spec)[]>[],
|
||||
Shaman: <(Character&Spec)[]>[],
|
||||
Warlock: <(Character&Spec)[]>[],
|
||||
Warrior: <(Character&Spec)[]>[],
|
||||
late: <(Character&Spec)[]>[],
|
||||
bench: <(Character&Spec)[]>[],
|
||||
},
|
||||
tokens:{}
|
||||
}
|
||||
|
||||
const subQuery = this.admin
|
||||
.knex('signups')
|
||||
.count('*')
|
||||
.where({
|
||||
raidid: this.admin.knex.ref('raids.id'),
|
||||
benched: false,
|
||||
late: false
|
||||
})
|
||||
.as('signupcount')
|
||||
|
||||
const raidInDb: Raid = await this.admin.knex('raids')
|
||||
.select('*', subQuery)
|
||||
.where('id','=',raid.id)
|
||||
.first()
|
||||
|
||||
const characterData: (Character & Spec & Signup)[] = await this.admin
|
||||
.knex('signups as s')
|
||||
.select('characterid as id', 'charactername', 'class', 'specname', 'race', 'userid', 'benched', 'late', 'raidid', 'characterid')
|
||||
.join('raids as r', 's.raidid','=','r.id')
|
||||
.join('characters as c', 's.characterid','=','c.id')
|
||||
.join('users as u', 'c.userid','=','u.id')
|
||||
.join('specs as sp', 'specid','=','sp.id')
|
||||
.where('r.id','=',raid.id)
|
||||
|
||||
characterData.forEach(data => {
|
||||
if(data.benched){
|
||||
ret.participants.bench.push(data)
|
||||
return
|
||||
}
|
||||
if(data.late){
|
||||
ret.participants.late.push(data)
|
||||
return
|
||||
}
|
||||
ret.participants[data.class].push(data)
|
||||
})
|
||||
|
||||
const tokenData: (Character & SRToken & Item)[] = await this.admin
|
||||
.knex('signups as s')
|
||||
.select('*')
|
||||
.join('raids as r', 's.raidid','=','r.id')
|
||||
.where('r.id','=',raid.id)
|
||||
.join('characters as c', 's.characterid','=','c.id')
|
||||
.join('tokens as t', 't.characterid','=','c.id')
|
||||
.join('items as i', 'i.id','=','t.itemid')
|
||||
|
||||
tokenData.forEach(data => {
|
||||
if(!ret.tokens[data.itemname])
|
||||
ret.tokens[data.itemname] = []
|
||||
ret.tokens[data.itemname].push(data)
|
||||
})
|
||||
return {
|
||||
...raidInDb,
|
||||
...ret
|
||||
}
|
||||
}
|
||||
|
||||
getSignups = async (raid:Raid) : Promise<Signup[]> => await this.admin
|
||||
.knex('signups')
|
||||
@@ -99,17 +254,60 @@ implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManag
|
||||
.select('*')
|
||||
.where('raidid', '=', raid.id!)
|
||||
|
||||
sign = async (usertoken:string, character:Character, raid:Raid) => {
|
||||
const maybeUserRecord = this.login.getUserRecordByToken(usertoken)
|
||||
sign = async (usertoken:string, character:Character, raid:Raid, late:boolean) => {
|
||||
const maybeUserRecord = this.userManager.getUserRecordByToken(usertoken)
|
||||
if(!maybeUserRecord || maybeUserRecord.user.id != character.userid){
|
||||
throw new Error("Bad Usertoken")
|
||||
}
|
||||
|
||||
await this.admin
|
||||
const exists = await this.admin
|
||||
.knex('signups')
|
||||
.insert({
|
||||
.select('*')
|
||||
.where({
|
||||
raidid: raid.id!,
|
||||
characterid: character.id!
|
||||
characterid: character.id!,
|
||||
})
|
||||
.first()
|
||||
|
||||
if(!exists){
|
||||
await this.admin
|
||||
.knex('signups')
|
||||
.insert({
|
||||
raidid: raid.id!,
|
||||
characterid: character.id!,
|
||||
late: late
|
||||
})
|
||||
}else{
|
||||
await this.admin
|
||||
.knex('signups')
|
||||
.update({
|
||||
raidid: raid.id!,
|
||||
characterid: character.id!,
|
||||
late: late
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
unsign = async (usertoken:string, character:Character, raid:Raid) => {
|
||||
const maybeUserRecord = this.userManager.getUserRecordByToken(usertoken)
|
||||
if(!maybeUserRecord || maybeUserRecord.user.id != character.userid){
|
||||
throw new Error("Bad Usertoken")
|
||||
}
|
||||
|
||||
await this.admin.knex('signups')
|
||||
.where({
|
||||
raidid: raid.id!,
|
||||
characterid: character.id!,
|
||||
})
|
||||
.del()
|
||||
}
|
||||
|
||||
setBenched = async (signup: Signup) : Promise<void> => {
|
||||
await this.admin.knex('signups')
|
||||
.where({
|
||||
raidid: signup.raidid,
|
||||
characterid: signup.characterid
|
||||
})
|
||||
.update(signup)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Raid, Signup, Character, RaidData } from "../../Types/Types"
|
||||
import { SubscriptionResponse } from "rpclibrary"
|
||||
|
||||
export type ShoutMessage = {
|
||||
message: string,
|
||||
sender: string,
|
||||
date: string
|
||||
}
|
||||
|
||||
export class IShoutbox{
|
||||
shout: (uuid:string, msg: ShoutMessage) => Promise<void>
|
||||
getFeed: () => Promise<ShoutMessage[]>
|
||||
subscribe: (callback) => Promise<SubscriptionResponse>
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { IShoutbox } from "./Interface"
|
||||
|
||||
export type ShoutboxIfc = {
|
||||
Shoutbox:{
|
||||
getFeed: IShoutbox['getFeed']
|
||||
shout: IShoutbox['shout']
|
||||
subscribe: IShoutbox['subscribe']
|
||||
}
|
||||
}
|
||||
|
||||
export type ShoutboxFeatureIfc = {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Injectable } from "../../Injector/ServiceDecorator";
|
||||
import { ShoutboxFeatureIfc, ShoutboxIfc } from "./RPCInterface";
|
||||
import { FrontworkComponent } from "../../Types/FrontworkComponent";
|
||||
import { TableDefiniton } from "../../Types/Types";
|
||||
import { IShoutbox, ShoutMessage } from "./Interface";
|
||||
import { SubscriptionResponse, makeSubResponse } from "rpclibrary";
|
||||
import * as CircularBuffer from "circular-buffer";
|
||||
|
||||
@Injectable(IShoutbox)
|
||||
export class Shoutbox
|
||||
implements FrontworkComponent<ShoutboxIfc, ShoutboxFeatureIfc>, IShoutbox{
|
||||
name = "Shoutbox" as "Shoutbox";
|
||||
|
||||
log: CircularBuffer = new CircularBuffer(500)
|
||||
subs = {}
|
||||
|
||||
exportRPCs = () => [
|
||||
this.shout,
|
||||
this.getFeed,
|
||||
{
|
||||
name: 'subscribe' as 'subscribe',
|
||||
hook: this.subscribe,
|
||||
onClose: (subres, rpcName) => {
|
||||
this.unsubscribe(subres.uuid)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
exportRPCFeatures = () => []
|
||||
|
||||
getTableDefinitions(): TableDefiniton[] {
|
||||
return []
|
||||
}
|
||||
|
||||
shout = async (uuid:string, msg: ShoutMessage) : Promise<void> => {
|
||||
if(!this.subs[uuid]) return
|
||||
this.broadcast(uuid, msg)
|
||||
this.log.push(msg)
|
||||
}
|
||||
|
||||
private broadcast = (uuid:string, msg: ShoutMessage) => {
|
||||
Object.values(this.subs).forEach((cb:any) => {
|
||||
try{
|
||||
cb(msg)
|
||||
}catch(e){
|
||||
this.unsubscribe(uuid)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
getFeed = async () : Promise<ShoutMessage[]> => {
|
||||
return this.log.toarray()
|
||||
}
|
||||
|
||||
subscribe = async (callback) : Promise<SubscriptionResponse> => {
|
||||
const resp = makeSubResponse({})
|
||||
this.subs[resp.uuid] = callback
|
||||
return resp
|
||||
}
|
||||
|
||||
unsubscribe = async (uuid: string) => {
|
||||
delete this.subs[uuid]
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -1,6 +1,6 @@
|
||||
import { Auth, Rank, User, RPCPermission, UserRecord } from "../../Types/Types"
|
||||
|
||||
export class ILoginManager{
|
||||
export class IUserManager{
|
||||
login: (username:string, pwHash:string) => Promise<Auth>
|
||||
logout: (username: string, tokenValue :string) => Promise<void>
|
||||
getAuth: (tokenValue: string) => Promise<Auth | void>
|
||||
@@ -9,4 +9,12 @@ export class ILoginManager{
|
||||
getPermissions: () => Promise<RPCPermission[]>
|
||||
checkToken: (token: string, rank: Rank) => boolean
|
||||
getUserRecordByToken: (tokenValue: string) => UserRecord | void
|
||||
getUser: (username: string) => Promise<User | void>
|
||||
|
||||
decrementCurrency: (user: User, value: number) => Promise<void>
|
||||
incrementCurrency: (user: User, value: number) => Promise<void>
|
||||
setCurrency: (user: User, value: number) => Promise<void>
|
||||
getCurrency: (user:User) => Promise<number>
|
||||
changeRank: (user:User, rank: Rank) => Promise<User>
|
||||
wipeCurrency: () => Promise<void>
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { IUserManager } from "./Interface"
|
||||
|
||||
export type UserManagerIfc = {
|
||||
UserManager: {
|
||||
checkToken: IUserManager['checkToken']
|
||||
login: IUserManager['login']
|
||||
logout: IUserManager['logout']
|
||||
createUser: IUserManager['createUser']
|
||||
getAuth: IUserManager['getAuth']
|
||||
getUser: IUserManager['getUser']
|
||||
}
|
||||
}
|
||||
|
||||
export type UserManagerFeatureIfc = {
|
||||
modifyPermissions: {
|
||||
setPermission: IUserManager['setPermission']
|
||||
getPermissions: IUserManager['getPermissions']
|
||||
changeRank: IUserManager['changeRank']
|
||||
}
|
||||
|
||||
softreserveCurrency: {
|
||||
incrementCurrency: IUserManager['incrementCurrency']
|
||||
decrementCurrency: IUserManager['decrementCurrency']
|
||||
setCurrency: IUserManager['setCurrency']
|
||||
}
|
||||
}
|
||||
+140
-65
@@ -1,32 +1,34 @@
|
||||
import { RPCServer, Socket } from "rpclibrary";
|
||||
import { Inject, Module } from "../../Injector/ServiceDecorator";
|
||||
import { Inject, Injectable } from "../../Injector/ServiceDecorator";
|
||||
import { FrontworkAdmin } from "../../Admin/Admin";
|
||||
import { GuildManager } from "../Guild/GuildManager";
|
||||
import { ItemManager } from "../Item/ItemManager";
|
||||
import { RaidManager } from "../Raid/RaidManager";
|
||||
import { CharacterManager } from "../Character/CharacterManager";
|
||||
import { LoginManagerIfc, LoginManagerFeatureIfc } from "./RPCInterface";
|
||||
import { UserManagerFeatureIfc, UserManagerIfc } from "./RPCInterface";
|
||||
import { FrontworkComponent } from "../../Types/FrontworkComponent";
|
||||
import { Rank, User, Auth, _Rank, TableDefiniton, RPCPermission, FrontcraftFeatureIfc, AnyRPCExporter, Token, UserRecord } from "../../Types/Types";
|
||||
import { IAdmin } from "../../Admin/Interface";
|
||||
import { ILoginManager } from "./Interface";
|
||||
import { IUserManager } from "./Interface";
|
||||
import { getLogger, Logger } from "log4js";
|
||||
import { saltedHash } from "../../Util/hash";
|
||||
|
||||
const uuid = require('uuid/v4')
|
||||
|
||||
const salt = "6pIbc6yjSN"
|
||||
const ONE_WEEK = 604800000
|
||||
|
||||
type Serverstate = {
|
||||
server: RPCServer,
|
||||
port : number,
|
||||
allowed: string[]
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@Module(ILoginManager)
|
||||
export class LoginManager
|
||||
implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginManager{
|
||||
name = "Authenticator" as "Authenticator";
|
||||
@Injectable(IUserManager)
|
||||
export class UserManager
|
||||
implements FrontworkComponent<UserManagerIfc, UserManagerFeatureIfc>, IUserManager{
|
||||
name = "UserManager" as "UserManager"
|
||||
|
||||
@Inject(IAdmin)
|
||||
private admin: FrontworkAdmin
|
||||
@@ -48,37 +50,44 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
|
||||
userLogins : {[username in string] : UserRecord} = {}
|
||||
|
||||
exportRPCs = () => [
|
||||
{
|
||||
name: 'login' as 'login',
|
||||
call: this.login
|
||||
},{
|
||||
name: 'logout' as 'logout',
|
||||
call: this.logout
|
||||
},{
|
||||
name: 'getAuth' as 'getAuth',
|
||||
call: this.getAuth
|
||||
},{
|
||||
name: 'checkToken' as 'checkToken',
|
||||
call: this.checkToken
|
||||
},{
|
||||
name: 'createUser' as 'createUser',
|
||||
call: this.createUser
|
||||
}
|
||||
this.login,
|
||||
this.logout,
|
||||
this.getAuth,
|
||||
this.checkToken,
|
||||
this.createUser,
|
||||
this.getUser
|
||||
]
|
||||
|
||||
exportRPCFeatures = () => [
|
||||
{
|
||||
name: 'modifyPermissions' as 'modifyPermissions',
|
||||
exportRPCs: () => [{
|
||||
name: 'getPermissions' as 'getPermissions',
|
||||
call: this.getPermissions
|
||||
},{
|
||||
name: 'setPermission' as 'setPermission',
|
||||
call: this.setPermission
|
||||
}]
|
||||
}
|
||||
]
|
||||
|
||||
exportRPCFeatures = () => [{
|
||||
name: 'modifyPermissions' as 'modifyPermissions',
|
||||
exportRPCs: () => [
|
||||
this.getPermissions,
|
||||
this.setPermission
|
||||
]
|
||||
},{
|
||||
name: 'softreserveCurrency' as 'softreserveCurrency',
|
||||
exportRPCs: () => [
|
||||
this.incrementCurrency,
|
||||
this.decrementCurrency,
|
||||
this.setCurrency
|
||||
]
|
||||
}]
|
||||
|
||||
changeRank = async (user:User, rank:Rank): Promise<User> => {
|
||||
await this.admin
|
||||
.knex('users')
|
||||
.where({
|
||||
user:user.username
|
||||
}).update({
|
||||
rank: rank
|
||||
})
|
||||
|
||||
return await this.admin
|
||||
.knex('users')
|
||||
.where({
|
||||
user: user.username
|
||||
}).first()
|
||||
}
|
||||
|
||||
getTableDefinitions = (): TableDefiniton[] => [
|
||||
{
|
||||
@@ -89,7 +98,7 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
|
||||
table.string("pwhash").notNullable()
|
||||
table.string("rank").notNullable()
|
||||
table.string("email").nullable().unique()
|
||||
table.boolean("locked").defaultTo(true)
|
||||
table.integer("currency").defaultTo(1)
|
||||
}
|
||||
},{
|
||||
name: 'rpcpermissions',
|
||||
@@ -109,19 +118,19 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
|
||||
initialize = async () => {
|
||||
this.exporters = [this.guild, this.item, this.raid, this.character]
|
||||
//set up permissions
|
||||
getLogger('LoginManager').debug('inserting permissions')
|
||||
getLogger('UserManager').debug('inserting permissions')
|
||||
|
||||
await Promise.all(
|
||||
[this, ...this.exporters].flatMap(exp => exp.exportRPCFeatures().map(async (feature) => {
|
||||
try{
|
||||
await this.admin.knex.insert({ rpcname: feature.name }).into('rpcpermissions')
|
||||
}catch(e){
|
||||
console.log(e);
|
||||
getLogger('UserManager').debug(feature.name);
|
||||
}
|
||||
})))
|
||||
|
||||
//start rankServers
|
||||
getLogger('LoginManager').debug('Starting rank servers')
|
||||
getLogger('UserManager').debug('Starting rank servers')
|
||||
|
||||
let rankServers = { } as any
|
||||
await Promise.all(_Rank.map(async (r,i) => {
|
||||
@@ -148,7 +157,7 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
|
||||
try{
|
||||
//return await state.server.destroy()
|
||||
}catch(e){
|
||||
getLogger('LoginManager').warn(e)
|
||||
getLogger('UserManager').warn(e)
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -171,7 +180,7 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
|
||||
while(!data){
|
||||
tries ++
|
||||
if(tries === 5){
|
||||
getLogger('LoginManager').debug('Connection check failed for connection *'+socket.port)
|
||||
getLogger('UserManager').debug('Connection check failed for connection *'+socket.port)
|
||||
socket.destroy()
|
||||
return false
|
||||
}
|
||||
@@ -189,7 +198,7 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
|
||||
|
||||
setPermission = async (permission: RPCPermission) => {
|
||||
await this.admin.knex('rpcpermissions')
|
||||
.where('rpcname', '=', permission.rpcnamename)
|
||||
.where('rpcname', '=', permission.rpcname)
|
||||
.update(permission)
|
||||
}
|
||||
|
||||
@@ -198,13 +207,14 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
|
||||
}
|
||||
|
||||
getPermission = async (feature: keyof FrontcraftFeatureIfc, rank:Rank) : Promise<boolean> => {
|
||||
const perm : RPCPermission[] = await this.admin.knex
|
||||
const perm : RPCPermission = await this.admin.knex
|
||||
.select(rank)
|
||||
.from('rpcpermissions')
|
||||
.where('rpcname', '=', <string>feature)
|
||||
.first()
|
||||
|
||||
if(perm.length === 0) return false
|
||||
return perm[0][rank]
|
||||
if(!perm) return false
|
||||
return perm[rank]
|
||||
}
|
||||
|
||||
getRPCForRank = async (rank: Rank): Promise<AnyRPCExporter[]> => {
|
||||
@@ -231,32 +241,33 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
|
||||
if(admins.length > 0){
|
||||
return {} as User
|
||||
}
|
||||
|
||||
user.locked = false
|
||||
}
|
||||
user.username = user.username.toLowerCase()
|
||||
user.pwhash = await saltedHash(user.pwhash, salt)
|
||||
|
||||
await this.admin.knex('users')
|
||||
.insert(user)
|
||||
|
||||
const users = await this.admin.knex
|
||||
const userRecord = await this.admin.knex
|
||||
.select("*")
|
||||
.from('users')
|
||||
.where(user)
|
||||
.first()
|
||||
|
||||
return users[0]
|
||||
return userRecord
|
||||
}
|
||||
|
||||
getUser = async (username: string) : Promise<User | void> => await this.admin
|
||||
.knex('users')
|
||||
.select('*')
|
||||
.where({
|
||||
username: username.toLowerCase()
|
||||
})
|
||||
.first()
|
||||
|
||||
logout = async (username:string, tokenValue : string) : Promise<void> => {
|
||||
try{
|
||||
username = username.toLowerCase()
|
||||
const maybeRecord = this.getUserRecordByToken(tokenValue)
|
||||
if(maybeRecord && maybeRecord.auth.user.username != username){
|
||||
getLogger('LoginManager').warn(`Bad logout attempt
|
||||
token by: ${maybeRecord.auth.user.username}
|
||||
tried to logout: ${username}`)
|
||||
return
|
||||
}
|
||||
if(!this.checkTokenOwnedByUser(username, tokenValue)) return
|
||||
|
||||
if(this.userLogins[username]){
|
||||
await Promise.all (Object.values(this.userLogins[username].connections).map(async (sock) => {
|
||||
@@ -275,15 +286,22 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
|
||||
}
|
||||
}
|
||||
|
||||
wipeCurrency = async () => {
|
||||
await this.admin.knex('users')
|
||||
.update({currency: 0})
|
||||
}
|
||||
|
||||
login = async(username:string, pwHash:string) : Promise<Auth> => {
|
||||
username = username.toLowerCase()
|
||||
const res:User[] = await this.admin.knex
|
||||
const user:User = await this.admin.knex
|
||||
.select('*')
|
||||
.from('users')
|
||||
.where({ username: username })
|
||||
.first()
|
||||
|
||||
const salted = await saltedHash(pwHash, salt)
|
||||
|
||||
if(res.length > 0 && pwHash === res[0].pwhash){
|
||||
const user:User = res[0]
|
||||
if(user && salted === user.pwhash){
|
||||
delete user.pwhash
|
||||
|
||||
//return existing auth
|
||||
@@ -339,11 +357,11 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
|
||||
}
|
||||
}).catch((e) => {
|
||||
socket.destroy();
|
||||
getLogger('LoginManager').warn(e);
|
||||
getLogger('UserManager').warn(e);
|
||||
})
|
||||
},
|
||||
errorHandler: (socket, e, rpcName, args) => {
|
||||
console.log(rpcName, args);
|
||||
getLogger('UserManager').error(rpcName, args, e);
|
||||
},
|
||||
sesame: (sesame) => this.checkToken(sesame, rank)
|
||||
})
|
||||
@@ -352,7 +370,7 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
|
||||
new Promise((res, rej) => setTimeout(res, 500))
|
||||
])
|
||||
if(!rpcServer && n>1)
|
||||
getLogger('LoginManager').warn("createServer retry nr.", n, 'port', port)
|
||||
getLogger('UserManager').warn("createServer retry nr.", n, 'port', port)
|
||||
}
|
||||
return rpcServer
|
||||
}
|
||||
@@ -360,6 +378,18 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
|
||||
checkToken = (token: string, rank: Rank) : boolean => this.rankServers[rank].allowed.includes(token)
|
||||
&& Object.values(this.userLogins).find(login => login.auth.token.value === token)!.auth.token.created > Date.now() - ONE_WEEK
|
||||
|
||||
checkTokenOwnedByUser = (username: string, tokenValue: string) => {
|
||||
username = username.toLowerCase()
|
||||
const maybeRecord = this.getUserRecordByToken(tokenValue)
|
||||
if(!maybeRecord || maybeRecord.auth.user.username != username){
|
||||
getLogger('UserManager').warn(`Bad logout attempt
|
||||
token by: ${maybeRecord?maybeRecord.auth.user.username:tokenValue}
|
||||
tried to logout: ${username}`)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
createToken = (user:User): Token => {
|
||||
|
||||
if(this.userLogins[user.username]){
|
||||
@@ -374,4 +404,49 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
|
||||
|
||||
return token
|
||||
}
|
||||
|
||||
getCurrency = async(user:User) : Promise<number> => {
|
||||
const usr : User = await this.admin
|
||||
.knex('users')
|
||||
.where('username', '=', user.username)
|
||||
.select('*')
|
||||
.first()
|
||||
|
||||
return usr.currency!
|
||||
}
|
||||
|
||||
decrementCurrency = async (user: User, value = 1) => {
|
||||
if(value < 1) return
|
||||
|
||||
const usr : User = await this.admin
|
||||
.knex('users')
|
||||
.where('id', '=', user.id)
|
||||
.select('*')
|
||||
.first()
|
||||
|
||||
if(!usr || usr.currency! <= 0) return
|
||||
|
||||
await this.admin
|
||||
.knex('users')
|
||||
.where('id', '=', user.id)
|
||||
.decrement('currency', value)
|
||||
}
|
||||
|
||||
incrementCurrency = async (user: User, value = 1) => {
|
||||
if(value < 1) return
|
||||
await this.admin
|
||||
.knex('users')
|
||||
.where('id', '=', user.id)
|
||||
.increment('currency', value)
|
||||
}
|
||||
|
||||
setCurrency = async (user: User, value: number) => {
|
||||
if(value < 0) return
|
||||
await this.admin
|
||||
.knex('users')
|
||||
.where('id', '=', user.id)
|
||||
.update('currency', value)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -51,8 +51,6 @@ export const Injector = new class {
|
||||
return rootobj
|
||||
}
|
||||
this.moduleObjs[target.name] = new target()
|
||||
return this.moduleObjs[target.name] as any
|
||||
|
||||
|
||||
return this.moduleObjs[target.name] as any
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,10 +6,10 @@ import { FrontworkComponent } from "../Types/FrontworkComponent";
|
||||
* @returns {GenericClassDecorator<Type<any>>}
|
||||
* @constructor
|
||||
*/
|
||||
export const Module = (ifc?: Type<any>) : GenericClassDecorator<Type<any>> => {
|
||||
export const Injectable = (_interface?: Type<any>) : GenericClassDecorator<Type<any>> => {
|
||||
return (target: Type<any>) => {
|
||||
Injector.modules.push({
|
||||
implements: ifc,
|
||||
implements: _interface,
|
||||
implementation: target
|
||||
})
|
||||
}
|
||||
@@ -20,12 +20,12 @@ export const Module = (ifc?: Type<any>) : GenericClassDecorator<Type<any>> => {
|
||||
* @constructor
|
||||
*/
|
||||
export const RootComponent = (config : {
|
||||
implements : Type<any>
|
||||
imports : Type<FrontworkComponent>[]
|
||||
injectable : Type<any>
|
||||
injects : Type<FrontworkComponent>[]
|
||||
}) : GenericClassDecorator<Type<any>> => {
|
||||
return (target: Type<any>) => {
|
||||
Injector.rootModules = config.imports
|
||||
Injector.rootInterface = config.implements
|
||||
Injector.rootModules = config.injects
|
||||
Injector.rootInterface = config.injectable
|
||||
Injector.root = target
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
import * as Knex from "knex"
|
||||
import { RPCExporter, Socket } from "rpclibrary";
|
||||
import { RaidManagerIfc, RaidManagerFeatureIfc } from "../Components/Raid/RPCInterface";
|
||||
import { LoginManagerIfc, LoginManagerFeatureIfc } from "../Components/Login/RPCInterface";
|
||||
import { UserManagerIfc, UserManagerFeatureIfc } from "../Components/User/RPCInterface";
|
||||
import { CharacterManagerIfc, CharacterManagerFeatureIfc } from "../Components/Character/RPCInterface";
|
||||
import { ItemManagerFeatureIfc, ItemManagerIfc } from "../Components/Item/RPCInterface";
|
||||
import { GuildManagerFeatureIfc, GuildManagerIfc } from "../Components/Guild/RPCInterface";
|
||||
import { ShoutboxIfc } from "../Components/Shoutbox/RPCInterface";
|
||||
|
||||
export type FrontcraftIfc = RaidManagerIfc
|
||||
& LoginManagerIfc
|
||||
& UserManagerIfc
|
||||
& CharacterManagerIfc
|
||||
& ItemManagerIfc
|
||||
& GuildManagerIfc
|
||||
& ShoutboxIfc
|
||||
|
||||
export type FrontcraftFeatureIfc = RaidManagerFeatureIfc
|
||||
& LoginManagerFeatureIfc
|
||||
& UserManagerFeatureIfc
|
||||
& CharacterManagerFeatureIfc
|
||||
& ItemManagerFeatureIfc
|
||||
& GuildManagerFeatureIfc
|
||||
|
||||
export declare type NotificationSeverity = 'Info' | 'Important' | 'Error';
|
||||
|
||||
@@ -25,6 +33,7 @@ export type TableDefiniton = {
|
||||
tableBuilder: (table: Knex.CreateTableBuilder) => void
|
||||
}
|
||||
|
||||
export type Race = "Human" | "Gnome" | "Night Elf" | "Dwarf"
|
||||
export type Rank = "ADMIN" | "Guildmaster" | "Officer" | "Classleader" | "Raider" | "Trial" | "Social" | "Guest"
|
||||
export const _Rank : Rank[] = ["ADMIN" , "Guildmaster" , "Officer" , "Classleader" , "Raider" , "Trial" , "Social" , "Guest"]
|
||||
export type Class = "Warrior" | "Rogue" | "Hunter" | "Mage" | "Warlock" | "Priest" | "Shaman" | "Paladin" | "Druid"
|
||||
@@ -33,11 +42,38 @@ export const _Class : Class[] = ["Warrior" , "Rogue" , "Hunter" , "Mage" , "Warl
|
||||
export type AnyRPCExporter = RPCExporter<any,any>
|
||||
|
||||
export type RPCPermission = {
|
||||
rpcnamename: string
|
||||
rpcname: string
|
||||
} & {
|
||||
[rank in Rank] : boolean
|
||||
}
|
||||
|
||||
export type RaidData = Raid & {
|
||||
participants: {
|
||||
[clazz in Class] : (Character & Spec)[]
|
||||
} & {
|
||||
late: (Character & Spec)[]
|
||||
bench: (Character & Spec)[]
|
||||
}
|
||||
tokens: {
|
||||
[itemname in string]: (Character & SRToken & Item)[]
|
||||
}
|
||||
}
|
||||
|
||||
export type SRToken = {
|
||||
characterid: number,
|
||||
itemid: number,
|
||||
level: number
|
||||
}
|
||||
|
||||
export type SRPriority = {
|
||||
id?:number
|
||||
race?:Race
|
||||
specid?:number,
|
||||
itemid?:number,
|
||||
description?:string,
|
||||
modifier:number
|
||||
}
|
||||
|
||||
export type Item = {
|
||||
id?:number
|
||||
itemname:string
|
||||
@@ -52,8 +88,7 @@ export type User = {
|
||||
username: string
|
||||
pwhash: string
|
||||
rank: Rank
|
||||
email?: string
|
||||
locked: boolean
|
||||
currency?: number
|
||||
}
|
||||
|
||||
export type Raid = {
|
||||
@@ -61,17 +96,21 @@ export type Raid = {
|
||||
title: string
|
||||
description: string
|
||||
start: string
|
||||
minrank: Rank
|
||||
signupcount?: number
|
||||
size: number
|
||||
}
|
||||
|
||||
export type Signup = {
|
||||
raidid: number
|
||||
characterid: number
|
||||
benched: boolean
|
||||
late: boolean
|
||||
}
|
||||
|
||||
export type Character = {
|
||||
id? : number
|
||||
charactername : string
|
||||
race: Race
|
||||
specid : number
|
||||
userid : number
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
const SHA256 = require("crypto-js/sha256");
|
||||
const Hex = require('crypto-js/enc-hex');
|
||||
|
||||
export async function saltedHash(input: string, salt: string) {
|
||||
const hash = await SHA256(input+salt)
|
||||
return Hex.stringify(hash)
|
||||
}
|
||||
Reference in New Issue
Block a user