diff --git a/package-lock.json b/package-lock.json index b6a01ff..807446b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -714,9 +714,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" }, "balanced-match": { "version": "1.0.0", @@ -1166,6 +1166,11 @@ "safe-buffer": "^5.0.1" } }, + "circular-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/circular-buffer/-/circular-buffer-1.0.2.tgz", + "integrity": "sha1-+g4VLtYp92/iTd+J5y69AHhsWm4=" + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -1465,6 +1470,11 @@ "randomfill": "^1.0.3" } }, + "crypto-js": { + "version": "3.1.9-1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz", + "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=" + }, "cyclist": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", @@ -5031,17 +5041,26 @@ "dev": true }, "npm-bundled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", - "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" }, "npm-packlist": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.6.tgz", - "integrity": "sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg==", + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", "requires": { "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" } }, "npm-run-path": { @@ -5752,9 +5771,9 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "psl": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", - "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==" }, "public-encrypt": { "version": "4.0.3", @@ -6111,9 +6130,9 @@ } }, "rpclibrary": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.6.2.tgz", - "integrity": "sha512-lQTU4XkB9CSHz7YgtAcpVfyR5XrmTRX4P4eQjK6DDUjqKSFvJb5ChXjHnt1BcaNWbJ2VqmhCNVbcoyunJ2u7Rg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.7.0.tgz", + "integrity": "sha512-HIL5YzFF53fACOWvI403hJZnGYoXLJGrGc0n1HYZH6rnnfEaBcogemZrCbyoOvnG204LH882e8VdpB+WBTEY/g==", "requires": { "bsock": "^0.1.9", "http": "0.0.0", @@ -6518,9 +6537,9 @@ "dev": true }, "sqlite3": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.1.0.tgz", - "integrity": "sha512-RvqoKxq+8pDHsJo7aXxsFR18i+dU2Wp5o12qAJOV5LNcDt+fgJsc2QKKg3sIRfXrN9ZjzY1T7SNe/DFVqAXjaw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.1.1.tgz", + "integrity": "sha512-CvT5XY+MWnn0HkbwVKJAyWEMfzpAPwnTiB3TobA5Mri44SrTovmmh499NPQP+gatkeOipqPlBLel7rn4E/PCQg==", "requires": { "nan": "^2.12.1", "node-pre-gyp": "^0.11.0", diff --git a/package.json b/package.json index d94eeb2..5373fc3 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,8 @@ "bsert": "0.0.10", "bsock": "^0.1.9", "child-process-promise": "^2.2.1", + "circular-buffer": "^1.0.2", + "crypto-js": "^3.1.9-1", "debug": "^4.1.1", "express": "^4.16.4", "frontblock": "^0.15.2", @@ -41,10 +43,10 @@ "path": "^0.12.7", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.0", - "rpclibrary": "^1.6.2", + "rpclibrary": "^1.7.0", "simple-git": "^1.124.0", "spawn-sync": "^2.0.0", - "sqlite3": "^4.1.0", + "sqlite3": "^4.1.1", "trash": "^6.0.0", "tsyringe": "^4.0.1", "upgiter": "^1.0.4", diff --git a/src/backend/Admin/Admin.ts b/src/backend/Admin/Admin.ts index a5deed1..fb8d619 100644 --- a/src/backend/Admin/Admin.ts +++ b/src/backend/Admin/Admin.ts @@ -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 diff --git a/src/backend/Admin/Interface.ts b/src/backend/Admin/Interface.ts index cd08f4b..aa67d3c 100644 --- a/src/backend/Admin/Interface.ts +++ b/src/backend/Admin/Interface.ts @@ -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 diff --git a/src/backend/Components/Character/CharacterManager.ts b/src/backend/Components/Character/CharacterManager.ts index ffdb88f..bdb3eaf 100644 --- a/src/backend/Components/Character/CharacterManager.ts +++ b/src/backend/Components/Character/CharacterManager.ts @@ -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, ICharacterManager{ name = "CharacterManager" as "CharacterManager"; @@ -17,35 +16,20 @@ implements FrontworkComponent, 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, 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, ICharacterMana return char }catch(e){ console.log(e); - } throw new Error('Unable to create character') } - getCharacters = async() : Promise => { - 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, 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, 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 => { + return await this.admin.knex('users').where({ + id: character.userid + }).first() + } + getSpecId = async (clazz: c, name: SpecT[c]) => await this.admin.knex .from('specs') .select('id') @@ -125,4 +120,20 @@ implements FrontworkComponent, ICharacterMana .first() .then(spec => spec.id) + getHeadCount = async(c:Class):Promise => { + 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', [ + 'Guest' + ]) + this.whereNot('c.alt', '1') + }) + .count('*') + return cnt[0]['count(*)'] as number + } } \ No newline at end of file diff --git a/src/backend/Components/Character/Interface.ts b/src/backend/Components/Character/Interface.ts index f46981a..bb7c8c9 100644 --- a/src/backend/Components/Character/Interface.ts +++ b/src/backend/Components/Character/Interface.ts @@ -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 getSpecId: (clazz: c, name: SpecT[c]) => Promise - getCharacters: () => Promise + getCharacters: () => Promise<(Character & Spec & User)[]> getCharacterByName: (charactername: string) => Promise<(Character & User & Spec) | void> getCharactersOfUser: (username: string) => Promise<(Character & Spec)[]> + getUserOfCharacter: (character:Character) => Promise + getHeadCount: (clazz: Class) => Promise } \ No newline at end of file diff --git a/src/backend/Components/Character/RPCInterface.ts b/src/backend/Components/Character/RPCInterface.ts index 55f7908..2ce318a 100644 --- a/src/backend/Components/Character/RPCInterface.ts +++ b/src/backend/Components/Character/RPCInterface.ts @@ -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'] - } + } \ No newline at end of file diff --git a/src/backend/Components/Debugger/Debugger.ts b/src/backend/Components/Debugger/Debugger.ts deleted file mode 100644 index 61de241..0000000 --- a/src/backend/Components/Debugger/Debugger.ts +++ /dev/null @@ -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{ - 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[] { - return [] - } - - getTableDefinitions(): TableDefiniton[] { - return [] - } - -} \ No newline at end of file diff --git a/src/backend/Components/Guild/GuildManager.ts b/src/backend/Components/Guild/GuildManager.ts index bd55a6a..8d664f8 100644 --- a/src/backend/Components/Guild/GuildManager.ts +++ b/src/backend/Components/Guild/GuildManager.ts @@ -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, IGuildManager{ @@ -32,26 +32,18 @@ implements FrontworkComponent, 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 = () => [] diff --git a/src/backend/Components/Item/Interface.ts b/src/backend/Components/Item/Interface.ts index 650fd2a..9704e6e 100644 --- a/src/backend/Components/Item/Interface.ts +++ b/src/backend/Components/Item/Interface.ts @@ -1,6 +1,14 @@ -import { Item } from "../../Types/Types" +import { Item, Character, SRToken, SRPriority, Spec } from "../../Types/Types" export class IItemManager{ getItems: () => Promise - getItem: (name:string) => Promise + fetchItem: (name:string) => Promise + buyToken: (usertoken: string, charactername:string, itemname:string) => Promise<(SRToken & Character & Item) | void> + setPriority: (itemname:string, priority: any) => Promise + calculatePriorities: (itemname: string, character:Character) => Promise + deletePriority: (priority:SRPriority) => Promise + getTokens: (character:Character) => Promise + getToken: (character:Character, item:Item) => Promise<(SRToken & Character & Item) | void> + getAllPriorities: () => Promise<(SRPriority & Spec & Item)[]> + wipeCurrencyAndItems: () => Promise } \ No newline at end of file diff --git a/src/backend/Components/Item/ItemManager.ts b/src/backend/Components/Item/ItemManager.ts index 09757f5..e0b35a1 100644 --- a/src/backend/Components/Item/ItemManager.ts +++ b/src/backend/Components/Item/ItemManager.ts @@ -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, TableDefinitionExporter, IItemManager{ name = "ItemManager" as "ItemManager"; @@ -21,23 +23,49 @@ implements FrontworkComponent, TableDefin @Inject(IAdmin) private admin: IAdmin - exportRPCs(): RPC[]{ - 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 => await this.admin.knex.select('*').from('items') - getItem = async (name:string):Promise => { + getItem = async(name:string):Promise => { + return await this.admin.knex('items').select('*').where('itemname','=',name).first() + } + + fetchItem = async (name:string):Promise => { 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, 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, 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 => { + await this.admin.knex('priorities') + .where(priority) + .del() + } + + setPriority = async(itemname:string, priority: SRPriority) : Promise => { + const item = await this.getItem(itemname) + + await this.admin + .knex('priorities') + .insert({ + itemid: item.id, + ...priority + }) + } + + getPriorities = async(itemname:string) : Promise => { + const item = await this.getItem(itemname) + + return await this.admin.knex('priorities') + .where('itemid', '=', item.id) + .select('*') + } + + calculatePriorities = async (itemname: string, character:Character):Promise=> { + 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 => { const count = await this.admin.knex('items').count('*'); return count[0]['count(*)'] @@ -86,15 +247,15 @@ implements FrontworkComponent, 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') diff --git a/src/backend/Components/Item/RPCInterface.ts b/src/backend/Components/Item/RPCInterface.ts index 288a4e4..475645e 100644 --- a/src/backend/Components/Item/RPCInterface.ts +++ b/src/backend/Components/Item/RPCInterface.ts @@ -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'] + } } \ No newline at end of file diff --git a/src/backend/Components/Login/RPCInterface.ts b/src/backend/Components/Login/RPCInterface.ts deleted file mode 100644 index 6304eb8..0000000 --- a/src/backend/Components/Login/RPCInterface.ts +++ /dev/null @@ -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'] - } -} diff --git a/src/backend/Components/RPCConfigLoader.ts b/src/backend/Components/RPCConfigLoader.ts index 23e959a..8d550a9 100644 --- a/src/backend/Components/RPCConfigLoader.ts +++ b/src/backend/Components/RPCConfigLoader.ts @@ -18,25 +18,12 @@ implements RPCExporter, "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 + ] } \ No newline at end of file diff --git a/src/backend/Components/Raid/Interface.ts b/src/backend/Components/Raid/Interface.ts index 0b77d21..6b9fe25 100644 --- a/src/backend/Components/Raid/Interface.ts +++ b/src/backend/Components/Raid/Interface.ts @@ -1,4 +1,4 @@ -import { Raid, Signup, Character } from "../../Types/Types" +import { Raid, Signup, Character, RaidData } from "../../Types/Types" export class IRaidManager{ getRaids: () => Promise @@ -6,5 +6,12 @@ export class IRaidManager{ addSignup: (signup: Signup) => Promise removeSignup: (signup: Signup) => Promise getSignups: (raid:Raid) => Promise - sign: (userToken: string, character:Character, raid:Raid, attending:boolean) => Promise + sign: (userToken: string, character:Character, raid:Raid, late:boolean) => Promise + unsign: (userToken: string, character:Character, raid:Raid,) => Promise + archiveRaid: (raid:Raid) => Promise + getRaidData: (raid:Raid) => Promise + setBenched: (signup: Signup) => Promise + getPastRaids: (limit: number) => Promise + getArchiveRaid: (id:number) => Promise + startRaid: (raid:Raid) => Promise } diff --git a/src/backend/Components/Raid/RPCInterface.ts b/src/backend/Components/Raid/RPCInterface.ts index f0c38c0..171dcf6 100644 --- a/src/backend/Components/Raid/RPCInterface.ts +++ b/src/backend/Components/Raid/RPCInterface.ts @@ -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'] } } \ No newline at end of file diff --git a/src/backend/Components/Raid/RaidManager.ts b/src/backend/Components/Raid/RaidManager.ts index a2dc6c8..7cabd50 100644 --- a/src/backend/Components/Raid/RaidManager.ts +++ b/src/backend/Components/Raid/RaidManager.ts @@ -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, IRaidManager{ name = "RaidManager" as "RaidManager"; @@ -14,37 +15,38 @@ implements FrontworkComponent, 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, 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, IRaidManag }) .delete() - getRaids = async () : Promise => await this.admin.knex - .select('*') - .from('raids') + getRaids = async () : Promise => { + + 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 => { + 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 => { + 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 => { + const data = await this.admin.knex('archive').select('raiddata').where({ + id: id + }).first() + + return JSON.parse(data.raiddata) + } + + getPastRaids = async(limit: number) : Promise => { + 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 => { + 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 => await this.admin .knex('signups') @@ -99,17 +254,60 @@ implements FrontworkComponent, 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 => { + await this.admin.knex('signups') + .where({ + raidid: signup.raidid, + characterid: signup.characterid + }) + .update(signup) } } \ No newline at end of file diff --git a/src/backend/Components/Shoutbox/Interface.ts b/src/backend/Components/Shoutbox/Interface.ts new file mode 100644 index 0000000..33eb3d4 --- /dev/null +++ b/src/backend/Components/Shoutbox/Interface.ts @@ -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 + getFeed: () => Promise + subscribe: (callback) => Promise +} diff --git a/src/backend/Components/Shoutbox/RPCInterface.ts b/src/backend/Components/Shoutbox/RPCInterface.ts new file mode 100644 index 0000000..2b5a3b7 --- /dev/null +++ b/src/backend/Components/Shoutbox/RPCInterface.ts @@ -0,0 +1,13 @@ +import { IShoutbox } from "./Interface" + +export type ShoutboxIfc = { + Shoutbox:{ + getFeed: IShoutbox['getFeed'] + shout: IShoutbox['shout'] + subscribe: IShoutbox['subscribe'] + } +} + +export type ShoutboxFeatureIfc = { + +} \ No newline at end of file diff --git a/src/backend/Components/Shoutbox/Shoutbox.ts b/src/backend/Components/Shoutbox/Shoutbox.ts new file mode 100644 index 0000000..f14a74b --- /dev/null +++ b/src/backend/Components/Shoutbox/Shoutbox.ts @@ -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, 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 => { + 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 => { + return this.log.toarray() + } + + subscribe = async (callback) : Promise => { + const resp = makeSubResponse({}) + this.subs[resp.uuid] = callback + return resp + } + + unsubscribe = async (uuid: string) => { + delete this.subs[uuid] + } +} \ No newline at end of file diff --git a/src/backend/Components/Login/Interface.ts b/src/backend/Components/User/Interface.ts similarity index 56% rename from src/backend/Components/Login/Interface.ts rename to src/backend/Components/User/Interface.ts index e4b9f72..952c5a5 100644 --- a/src/backend/Components/Login/Interface.ts +++ b/src/backend/Components/User/Interface.ts @@ -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 logout: (username: string, tokenValue :string) => Promise getAuth: (tokenValue: string) => Promise @@ -9,4 +9,12 @@ export class ILoginManager{ getPermissions: () => Promise checkToken: (token: string, rank: Rank) => boolean getUserRecordByToken: (tokenValue: string) => UserRecord | void + getUser: (username: string) => Promise + + decrementCurrency: (user: User, value: number) => Promise + incrementCurrency: (user: User, value: number) => Promise + setCurrency: (user: User, value: number) => Promise + getCurrency: (user:User) => Promise + changeRank: (user:User, rank: Rank) => Promise + wipeCurrency: () => Promise } \ No newline at end of file diff --git a/src/backend/Components/User/RPCInterface.ts b/src/backend/Components/User/RPCInterface.ts new file mode 100644 index 0000000..971d9a5 --- /dev/null +++ b/src/backend/Components/User/RPCInterface.ts @@ -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'] + } +} diff --git a/src/backend/Components/Login/LoginManager.ts b/src/backend/Components/User/UserManager.ts similarity index 69% rename from src/backend/Components/Login/LoginManager.ts rename to src/backend/Components/User/UserManager.ts index 1757046..417e84f 100644 --- a/src/backend/Components/Login/LoginManager.ts +++ b/src/backend/Components/User/UserManager.ts @@ -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, ILoginManager{ - name = "Authenticator" as "Authenticator"; +@Injectable(IUserManager) +export class UserManager +implements FrontworkComponent, IUserManager{ + name = "UserManager" as "UserManager" @Inject(IAdmin) private admin: FrontworkAdmin @@ -48,37 +50,44 @@ implements FrontworkComponent, 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 => { + 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, 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, 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, ILoginMa try{ //return await state.server.destroy() }catch(e){ - getLogger('LoginManager').warn(e) + getLogger('UserManager').warn(e) } }) ); @@ -171,7 +180,7 @@ implements FrontworkComponent, 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, 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, ILoginMa } getPermission = async (feature: keyof FrontcraftFeatureIfc, rank:Rank) : Promise => { - const perm : RPCPermission[] = await this.admin.knex + const perm : RPCPermission = await this.admin.knex .select(rank) .from('rpcpermissions') .where('rpcname', '=', feature) + .first() - if(perm.length === 0) return false - return perm[0][rank] + if(!perm) return false + return perm[rank] } getRPCForRank = async (rank: Rank): Promise => { @@ -231,32 +241,33 @@ implements FrontworkComponent, 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 => await this.admin + .knex('users') + .select('*') + .where({ + username: username.toLowerCase() + }) + .first() + logout = async (username:string, tokenValue : string) : Promise => { 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, ILoginMa } } + wipeCurrency = async () => { + await this.admin.knex('users') + .update({currency: 0}) + } + login = async(username:string, pwHash:string) : Promise => { 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, 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, 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, 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, ILoginMa return token } + + getCurrency = async(user:User) : Promise => { + 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) + } + + } \ No newline at end of file diff --git a/src/backend/Injector/Injector.ts b/src/backend/Injector/Injector.ts index 154ce60..633a96d 100644 --- a/src/backend/Injector/Injector.ts +++ b/src/backend/Injector/Injector.ts @@ -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 } }; diff --git a/src/backend/Injector/ServiceDecorator.ts b/src/backend/Injector/ServiceDecorator.ts index 60bffc3..66690a0 100644 --- a/src/backend/Injector/ServiceDecorator.ts +++ b/src/backend/Injector/ServiceDecorator.ts @@ -6,10 +6,10 @@ import { FrontworkComponent } from "../Types/FrontworkComponent"; * @returns {GenericClassDecorator>} * @constructor */ -export const Module = (ifc?: Type) : GenericClassDecorator> => { +export const Injectable = (_interface?: Type) : GenericClassDecorator> => { return (target: Type) => { Injector.modules.push({ - implements: ifc, + implements: _interface, implementation: target }) } @@ -20,12 +20,12 @@ export const Module = (ifc?: Type) : GenericClassDecorator> => { * @constructor */ export const RootComponent = (config : { - implements : Type - imports : Type[] + injectable : Type + injects : Type[] }) : GenericClassDecorator> => { return (target: Type) => { - Injector.rootModules = config.imports - Injector.rootInterface = config.implements + Injector.rootModules = config.injects + Injector.rootInterface = config.injectable Injector.root = target } } diff --git a/src/backend/Types/Types.ts b/src/backend/Types/Types.ts index 0fe411b..5621aef 100644 --- a/src/backend/Types/Types.ts +++ b/src/backend/Types/Types.ts @@ -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 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 } diff --git a/src/backend/Util/hash.ts b/src/backend/Util/hash.ts new file mode 100644 index 0000000..32a1ae8 --- /dev/null +++ b/src/backend/Util/hash.ts @@ -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) +} \ No newline at end of file diff --git a/src/frontend/src/app/@core/core.module.ts b/src/frontend/src/app/@core/core.module.ts index 0615cfb..0e0dbca 100644 --- a/src/frontend/src/app/@core/core.module.ts +++ b/src/frontend/src/app/@core/core.module.ts @@ -9,15 +9,15 @@ import { PlayerService, StateService, } from './utils'; -import { LoginApiService, initializeLoginSvc } from '../frontcraft/services/login-api'; +import { ApiService, initializeLoginSvc } from '../frontcraft/services/login-api'; const DATA_SERVICES = [ - {provide: LoginApiService, useClass: LoginApiService}, + {provide: ApiService, useClass: ApiService}, {provide: CookieService, useClass: CookieService}, { provide: APP_INITIALIZER, useFactory: initializeLoginSvc, - deps: [LoginApiService, CookieService], + deps: [ApiService, CookieService], multi: true } ]; diff --git a/src/frontend/src/app/@theme/components/header/chat.component.ts b/src/frontend/src/app/@theme/components/header/chat.component.ts new file mode 100644 index 0000000..8905ea5 --- /dev/null +++ b/src/frontend/src/app/@theme/components/header/chat.component.ts @@ -0,0 +1,43 @@ +import { Component } from '@angular/core'; +import { ApiService } from '../../../frontcraft/services/login-api'; +import { ShoutMessage } from '../../../../../../backend/Components/Shoutbox/Interface'; + +@Component({ + selector: 'chat', + template: ` + + + + + + + `, + styles: [` + nb-chat { + width: 600px; + margin: 0.5rem 0 2rem 2rem; + }`], +}) +export class ChatComponent { + + messages : ShoutMessage[] = [] + history + + constructor( + private api: ApiService + ) {} + + submit(event){ + this.sendMessage({ + date: ""+Date.now(), + message: event.message, + sender: this.api.getCurrentUser().username + }) + } + + sendMessage : (msg:ShoutMessage)=>Promise +} diff --git a/src/frontend/src/app/@theme/components/header/header.component.html b/src/frontend/src/app/@theme/components/header/header.component.html index d902992..4ecff79 100644 --- a/src/frontend/src/app/@theme/components/header/header.component.html +++ b/src/frontend/src/app/@theme/components/header/header.component.html @@ -3,16 +3,27 @@ - +
- - - + + + + + = new Subject(); - userPictureOnly: boolean = false; user: User; + title = "Loading ... "; themes = [ { @@ -38,37 +41,46 @@ export class HeaderComponent implements OnInit, OnDestroy { ]; currentTheme = 'dark'; - userMenu : NbMenuItem[] = [ { title: 'Log out', link: '/auth/logout' } ]; + sendMessage: any = console.log + chatwindow: ChatComponent + chatlog: ShoutMessage[] = [] + newmessage = false + lastmessage = "asdasd" + constructor(private sidebarService: NbSidebarService, - private menuService: NbMenuService, + private router: Router, private themeService: NbThemeService, private layoutService: LayoutService, private breakpointService: NbMediaBreakpointsService, - private loginService: LoginApiService, + private api: ApiService, + private dialogService : NbDialogService ) {} ngOnInit() { this.currentTheme = this.themeService.currentTheme; - this.user = this.loginService.getCurrentUser() + this.user = this.api.getCurrentUser() if(this.user) this.userMenu.unshift({ title: 'Profile', link: '/frontcraft/user/'+this.user.username }); - - /* - this.userService.getUsers() - .pipe(takeUntil(this.destroy$)) - .subscribe((users: any) => this.user = users.lee); - */ - const { xl } = this.breakpointService.getBreakpointsMap(); - this.themeService.onMediaQueryChange() - .pipe( - map(([, currentBreakpoint]) => currentBreakpoint.width < xl), - takeUntil(this.destroy$), - ) - .subscribe((isLessThanXl: boolean) => this.userPictureOnly = isLessThanXl); + this.api.get('GuildManager').getGuildInfo().then(info => { + this.title = info.name + }) + + this.api.connectShoutbox((msg) => { + this.chatlog.push(msg) + if(msg.message != this.lastmessage) + this.newmessage = true + if(this.chatwindow) this.chatwindow.messages.push(msg) + }).then(sendMsg => { + this.sendMessage = (msg) => { + sendMsg(msg) + this.lastmessage = msg.message + } + this.api.get('Shoutbox').getFeed().then(log => {this.chatlog = log}) + }) this.themeService.onThemeChange() .pipe( @@ -78,6 +90,20 @@ export class HeaderComponent implements OnInit, OnDestroy { .subscribe(themeName => this.currentTheme = themeName); } + openChat(){ + this.newmessage = false + const ref = this.dialogService.open(ChatComponent, { + context: { + sendMessage: this.sendMessage, + } + }); + ref.onClose.subscribe(()=>{ + this.chatwindow = null + }) + this.chatwindow = ref.componentRef.instance + this.chatwindow.messages.push(...this.chatlog) + } + ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); @@ -95,11 +121,11 @@ export class HeaderComponent implements OnInit, OnDestroy { } logout() { - this.loginService.logout() + this.api.logout() } navigateHome() { - this.menuService.navigateHome(); + this.router.navigateByUrl('/') return false; } } diff --git a/src/frontend/src/app/@theme/theme.module.ts b/src/frontend/src/app/@theme/theme.module.ts index 4aacc66..1e484a5 100644 --- a/src/frontend/src/app/@theme/theme.module.ts +++ b/src/frontend/src/app/@theme/theme.module.ts @@ -12,6 +12,9 @@ import { NbSelectModule, NbIconModule, NbThemeModule, + NbDialogModule, + NbChatModule, + NbCardModule, } from '@nebular/theme'; import { NbEvaIconsModule } from '@nebular/eva-icons'; import { NbSecurityModule } from '@nebular/security'; @@ -39,6 +42,7 @@ import { DEFAULT_THEME } from './styles/theme.default'; import { COSMIC_THEME } from './styles/theme.cosmic'; import { CORPORATE_THEME } from './styles/theme.corporate'; import { DARK_THEME } from './styles/theme.dark'; +import { ChatComponent } from './components/header/chat.component'; const NB_MODULES = [ NbLayoutModule, @@ -53,6 +57,9 @@ const NB_MODULES = [ NbSelectModule, NbIconModule, NbEvaIconsModule, + NbChatModule, + NbCardModule, + NbDialogModule.forChild() ]; const COMPONENTS = [ HeaderComponent, @@ -62,7 +69,8 @@ const COMPONENTS = [ OneColumnLayoutComponent, ThreeColumnsLayoutComponent, TwoColumnsLayoutComponent, - OneColumnNoSidebarLayoutComponent + OneColumnNoSidebarLayoutComponent, + ChatComponent ]; const PIPES = [ CapitalizePipe, @@ -76,6 +84,9 @@ const PIPES = [ imports: [CommonModule, ...NB_MODULES], exports: [CommonModule, ...PIPES, ...COMPONENTS], declarations: [...COMPONENTS, ...PIPES], + entryComponents: [ + ChatComponent + ] }) export class ThemeModule { static forRoot(): ModuleWithProviders { diff --git a/src/frontend/src/app/app.component.ts b/src/frontend/src/app/app.component.ts index 7eec052..04f1f74 100644 --- a/src/frontend/src/app/app.component.ts +++ b/src/frontend/src/app/app.component.ts @@ -5,7 +5,7 @@ */ import { Component, OnInit } from '@angular/core'; import { AnalyticsService } from './@core/utils/analytics.service'; -import { LoginApiService } from './frontcraft/services/login-api'; +import { ApiService } from './frontcraft/services/login-api'; @Component({ selector: 'ngx-app', @@ -13,7 +13,7 @@ import { LoginApiService } from './frontcraft/services/login-api'; }) export class AppComponent implements OnInit { - constructor(private loginSvc: LoginApiService, private analytics: AnalyticsService) { + constructor(private loginSvc: ApiService, private analytics: AnalyticsService) { } diff --git a/src/frontend/src/app/app.module.ts b/src/frontend/src/app/app.module.ts index 9ec1715..e868573 100644 --- a/src/frontend/src/app/app.module.ts +++ b/src/frontend/src/app/app.module.ts @@ -5,7 +5,7 @@ */ import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { NgModule } from '@angular/core'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; import { CoreModule } from './@core/core.module'; import { ThemeModule } from './@theme/theme.module'; @@ -20,6 +20,8 @@ import { NbToastrModule, NbWindowModule, } from '@nebular/theme'; +import { ApiService, initializeLoginSvc } from './frontcraft/services/login-api'; +import { CookieService } from 'ngx-cookie-service'; @NgModule({ declarations: [AppComponent], @@ -42,7 +44,13 @@ import { ], bootstrap: [AppComponent], providers: [ - + ApiService, + { + provide: APP_INITIALIZER, + deps: [CookieService], + multi: true, + useFactory: initializeLoginSvc + }, ] }) export class AppModule { diff --git a/src/frontend/src/app/frontcraft/auth/auth-layout.component.ts b/src/frontend/src/app/frontcraft/auth/auth-layout.component.ts index a08eb3b..a9d5cbc 100644 --- a/src/frontend/src/app/frontcraft/auth/auth-layout.component.ts +++ b/src/frontend/src/app/frontcraft/auth/auth-layout.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { LoginApiService } from '../services/login-api'; +import { ApiService } from '../services/login-api'; import { Router } from '@angular/router'; @Component({ @@ -13,7 +13,7 @@ import { Router } from '@angular/router'; export class AuthComponent implements OnInit{ constructor( - private loginSvc : LoginApiService, + private loginSvc : ApiService, private router: Router ){} diff --git a/src/frontend/src/app/frontcraft/auth/login/login.component.html b/src/frontend/src/app/frontcraft/auth/login/login.component.html index 4bb42b4..ba37aac 100644 --- a/src/frontend/src/app/frontcraft/auth/login/login.component.html +++ b/src/frontend/src/app/frontcraft/auth/login/login.component.html @@ -3,7 +3,7 @@
- + -
- - -
-
{ + this.api.checkLogin().then(loggedin => { if(loggedin){ this.router.navigateByUrl("/") } @@ -60,27 +60,27 @@ export class RegisterComponent implements OnInit{ this.character = {} try{ - const usr = await this.loginApi.getUnprivilegedSocket().Authenticator.createUser(user) + const usr = await this.api.get('UserManager').createUser(user) }catch(e){ alert("Error creating user"+e) return } try{ - await this.loginApi.login(user.username, pw) - await this.loginApi.getFeature('createCharacter').then(async feature => { - if(!feature) return - const specid = await this.loginApi.getUnprivilegedSocket().CharacterManager.getSpecId(char['class'], char['spec']) - await feature.createCharacter(this.loginApi.getAuth().token.value, { - charactername: char.name, - specid: specid, - userid: this.loginApi.getAuth().user.id! - }) + await this.api.login(user.username, pw) + + const characterManager = this.api.get('CharacterManager') + + const specid = await characterManager.getSpecId(char['class'], char['spec']) + await characterManager.createCharacter(this.api.getAuth().token.value, { + charactername: char.name, + specid: specid, + userid: this.api.getAuth().user.id!, + race: 'Human' }) }catch(e){ alert("Error creating character"+e) return } } - } \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/character/character.component.html b/src/frontend/src/app/frontcraft/pages/character/character.component.html index 5951352..2a4da72 100644 --- a/src/frontend/src/app/frontcraft/pages/character/character.component.html +++ b/src/frontend/src/app/frontcraft/pages/character/character.component.html @@ -1,10 +1,23 @@ {{char.charactername}} + {{char.race}}
{{char.specname}} {{char.class}}
Owned by {{char.username}} ({{char.rank}}) +

+ + [ {{token.level}} ] + + + {{token.itemname}} +
+
diff --git a/src/frontend/src/app/frontcraft/pages/character/character.component.ts b/src/frontend/src/app/frontcraft/pages/character/character.component.ts index e99d709..104ed73 100644 --- a/src/frontend/src/app/frontcraft/pages/character/character.component.ts +++ b/src/frontend/src/app/frontcraft/pages/character/character.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { LoginApiService } from '../../services/login-api'; +import { ApiService as ApiService } from '../../services/login-api'; import { Spec, User, Character } from '../../../../../../backend/Types/Types'; import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; @@ -12,22 +12,24 @@ export class FrontcraftCharacterComponent implements OnInit{ char : (Character & User & Spec) = {} as any color : string + tokens constructor( - private login: LoginApiService, + private api: ApiService, private route: ActivatedRoute, - private router: Router, ){} async ngOnInit(){ const param = this.route.snapshot.paramMap.get('name'); - this.login.getUnprivilegedSocket() - .CharacterManager + this.api.get('CharacterManager') .getCharacterByName(param) .then((char) => { if(char){ this.color = getClassColor(char.class) this.char = char + this.api.get('ItemManager').getTokens(this.char).then(tokens => { + this.tokens = tokens + }) } }) } diff --git a/src/frontend/src/app/frontcraft/pages/characters/characters.component.html b/src/frontend/src/app/frontcraft/pages/characters/characters.component.html new file mode 100644 index 0000000..2f40914 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/characters/characters.component.html @@ -0,0 +1,34 @@ + + + +
+ +
+
+ + + + + + + + + + + + + + +
+ + {{character.charactername}} (Alt) + + + {{character.race}} + + {{character.specname}} + + {{character.class}} +
+
+
\ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/characters/characters.component.ts b/src/frontend/src/app/frontcraft/pages/characters/characters.component.ts new file mode 100644 index 0000000..ef3e4e9 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/characters/characters.component.ts @@ -0,0 +1,123 @@ +import { Component, OnInit, AfterViewInit, OnDestroy } from '@angular/core'; +import { NbThemeService } from '@nebular/theme'; +import { ApiService } from '../../services/login-api'; +import { _Class } from '../../../../../../backend/Types/Types'; +import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; + + +@Component({ + selector: 'characters', + templateUrl: './characters.component.html', +}) +export class FrontcraftCharactersComponent implements AfterViewInit, OnDestroy{ + options: any = {}; + themeSubscription: any; + search = "" + + allcharacters = [] + displayedcharacters = [] + + constructor(private theme: NbThemeService, private api:ApiService) { + } + + changeSearch(){ + if(!this.search || this.search == "") + this.displayedcharacters = this.allcharacters + else{ + const searchterm = this.search.toLocaleLowerCase() + this.displayedcharacters = this.allcharacters.filter(char => + char.charactername.toLowerCase().includes(searchterm) || + char.class.toLowerCase().includes(searchterm) || + char.specname.toLowerCase().includes(searchterm) || + char.race.toLowerCase().includes(searchterm) + ) + } + } + + onChartClick(event){ + this.search = event.name + this.changeSearch() + } + + ngAfterViewInit() { + this.themeSubscription = this.theme.getJsTheme().subscribe(async config => { + const data = {} + + await this.api.get('CharacterManager').getCharacters().then(chars => { + chars.forEach(char => { + char['color'] = getClassColor(char.class) + }) + this.allcharacters = chars + this.displayedcharacters = chars + this.search="" + }) + + await Promise.all(_Class.map(async cls => { + const c = await this.api.get('CharacterManager').getHeadCount(cls) + data[cls] = c + })) + + const echarts: any = config.variables.echarts; + this.options = { + backgroundColor: echarts.bg, + color: [ + "#FFF569", + "#C79C6E", + "#FFFFFF", + "#40C7EB", + "#A9D271", + "#F58CBA", + "#FF7D0A", + "#8787ED" + ], + tooltip: { + trigger: 'item', + formatter: '{b} : {c} ({d}%)', + }, + series: [ + { + name: 'Classes', + type: 'pie', + radius: '80%', + center: ['50%', '50%'], + data: [ + { value: data['Rogue'], name: 'Rogue' }, + { value: data['Warrior'], name: 'Warrior' }, + { value: data['Priest'], name: 'Priest' }, + { value: data['Mage'], name: 'Mage' }, + { value: data['Hunter'], name: 'Hunter' }, + { value: data['Paladin'], name: 'Paladin' }, + { value: data['Druid'], name: 'Druid' }, + { value: data['Warlock'], name: 'Warlock' }, + ], + itemStyle: { + emphasis: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: echarts.itemHoverShadowColor, + }, + }, + label: { + normal: { + textStyle: { + color: echarts.textColor, + }, + }, + }, + labelLine: { + normal: { + lineStyle: { + color: echarts.axisLineColor, + }, + }, + }, + }, + ], + }; + }); + } + + ngOnDestroy(): void { + this.themeSubscription.unsubscribe(); + } +} diff --git a/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.html b/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.html deleted file mode 100644 index 4362e2a..0000000 --- a/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -
- {{customColumn}} - - - - - {{row.data[customColumn]}} - - {{column}} - {{row.data[column] || '-'}}
-
-
\ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.scss b/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.scss deleted file mode 100644 index 5667cf7..0000000 --- a/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.scss +++ /dev/null @@ -1,42 +0,0 @@ -button[nbTreeGridRowToggle] { - background: transparent; - border: none; - padding: 0; - } - - .search-label { - display: block; - } - .search-input { - margin-bottom: 1rem; - } - - .nb-column-name { - width: 100%; - } - - @media screen and (min-width: 400px) { - .nb-column-name, - .nb-column-size { - width: 50%; - } - } - - @media screen and (min-width: 500px) { - .nb-column-name, - .nb-column-size, - .nb-column-kind { - width: 33.333%; - } - } - - @media screen and (min-width: 600px) { - .nb-column-name { - width: 31%; - } - .nb-column-size, - .nb-column-kind, - .nb-column-items { - width: 23%; - } - } \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.ts b/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.ts deleted file mode 100644 index 365631c..0000000 --- a/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Component } from '@angular/core'; -import { NbSortDirection, NbSortRequest, NbTreeGridDataSourceBuilder, NbTreeGridDataSource } from '@nebular/theme'; - -interface TreeNode { - data: T; - children?: TreeNode[]; - expanded?: boolean; -} - -interface Row { - name: string, - character: any, - SRC: any, -} - -type TreeType = TreeNode - -@Component({ - selector: 'dashboard', - templateUrl: './dashboard.component.html', - styleUrls: ['./tree-grid-shared.scss', './dashboard.component.scss'], -}) -export class FrontcraftDashboardComponent{ - customColumn = 'name'; - defaultColumns = [ 'character', 'SRC'/*, 'Profile'*/ ]; - allColumns = [ this.customColumn, ...this.defaultColumns ]; - - dataSource: NbTreeGridDataSource; - - sortColumn: string; - sortDirection: NbSortDirection = NbSortDirection.NONE; - - constructor(private dataSourceBuilder: NbTreeGridDataSourceBuilder) { - this.dataSource = this.dataSourceBuilder.create(this.data); - } - - updateSort(sortRequest: NbSortRequest): void { - this.sortColumn = sortRequest.column; - this.sortDirection = sortRequest.direction; - } - - getSortDirection(column: string): NbSortDirection { - if (this.sortColumn === column) { - return this.sortDirection; - } - return NbSortDirection.NONE; - } - - private data: TreeType[] = [ - { - data: {name: 'a', character: 2, SRC: 0}, - children: [ - {data: {name: 'a', character: 'Warrior', SRC: 'Arms'}} - ] - } - ]; - - getShowOn(index: number) { - const minWithForMultipleColumns = 400; - const nextColumnStep = 100; - return minWithForMultipleColumns + (nextColumnStep * index); - } -} diff --git a/src/frontend/src/app/frontcraft/pages/dashboard/tree-grid-shared.scss b/src/frontend/src/app/frontcraft/pages/dashboard/tree-grid-shared.scss deleted file mode 100644 index e490e9a..0000000 --- a/src/frontend/src/app/frontcraft/pages/dashboard/tree-grid-shared.scss +++ /dev/null @@ -1,10 +0,0 @@ -::ng-deep { - body { - min-height: 20rem; - } - - .nb-tree-grid-header-cell, - .nb-tree-grid-header-cell button { - text-transform: capitalize; - } - } \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts b/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts index d110615..4b9f1e2 100644 --- a/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts +++ b/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts @@ -1,6 +1,6 @@ -import { Component, AfterContentChecked, OnInit } from '@angular/core'; +import { Component, AfterContentChecked, OnInit, AfterContentInit } from '@angular/core'; import { NbMenuItem } from '@nebular/theme'; -import { LoginApiService } from '../services/login-api'; +import { ApiService } from '../services/login-api'; import { Router } from '@angular/router'; @Component({ @@ -9,35 +9,35 @@ import { Router } from '@angular/router'; - - + `, }) -export class PagesLayoutComponent implements OnInit{ +export class PagesLayoutComponent implements AfterContentInit{ menu:NbMenuItem[] = [{ - icon: 'people-outline', - title: 'People', - link: '/frontcraft/people' + icon: 'image', + title: 'Raids', + link: '/frontcraft/raids', },{ - icon: 'clock-outline', - title: 'Raids' + icon: 'people', + title: 'Characters', + link: '/frontcraft/characters', },{ - icon: 'clock-outline', - title: 'character', - link: '/frontcraft/character/a' + icon: 'shopping-cart', + title: 'Token Shop', + link: '/frontcraft/shop', },{ - icon: 'clock-outline', - title: 'user', - link: '/frontcraft/user/a' + icon: 'book-open-outline', + title: 'Loot Rules', + link: '/frontcraft/rules', }] constructor( - private loginSvc : LoginApiService, + private loginSvc : ApiService, private router : Router ){} - ngOnInit() : void { + ngAfterContentInit() : void { this.loginSvc.checkLogin().then(loggedin => { if(!loggedin){ this.router.navigateByUrl("/auth") diff --git a/src/frontend/src/app/frontcraft/pages/pages-routing.module.ts b/src/frontend/src/app/frontcraft/pages/pages-routing.module.ts index 336d30e..f95c932 100644 --- a/src/frontend/src/app/frontcraft/pages/pages-routing.module.ts +++ b/src/frontend/src/app/frontcraft/pages/pages-routing.module.ts @@ -1,35 +1,55 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { FrontcraftDashboardComponent } from './dashboard/dashboard.component'; import { PagesLayoutComponent } from './pages-layout.component'; import { FrontcraftCharacterComponent } from './character/character.component'; import { FrontcraftUserComponent } from './user/user.component'; -import { FrontcraftPeopleComponent } from './people/people.component'; +import { FrontcraftRaidsComponent } from './raids/raids.component'; +import { FrontcraftRaidComponent } from './raid/raid.component'; +import { FrontcraftArchiveComponent } from './raid/archive.component'; +import { FrontcraftShopComponent } from './shop/shop.component'; +import { FrontcraftRulesComponent } from './rules/rules.component'; +import { FrontcraftCharactersComponent } from './characters/characters.component'; export const routes: Routes = [ { path: '', component: PagesLayoutComponent, children: [ - { - path: 'people', - component: FrontcraftPeopleComponent - }, { path: 'user/:name', component: FrontcraftUserComponent }, + { + path: 'raid/:id', + component: FrontcraftRaidComponent + }, + { + path: 'archive/:id', + component: FrontcraftArchiveComponent + }, + { + path: 'raids', + component: FrontcraftRaidsComponent + }, { path: 'character/:name', component: FrontcraftCharacterComponent }, { - path: 'dashboard', - component: FrontcraftDashboardComponent, + path: 'characters', + component: FrontcraftCharactersComponent, + }, + { + path: 'shop', + component: FrontcraftShopComponent + }, + { + path: 'rules', + component: FrontcraftRulesComponent }, { path: '**', - redirectTo: 'dashboard' + redirectTo: 'raids' }, ] } diff --git a/src/frontend/src/app/frontcraft/pages/pages.module.ts b/src/frontend/src/app/frontcraft/pages/pages.module.ts index cbcb8ad..941968b 100644 --- a/src/frontend/src/app/frontcraft/pages/pages.module.ts +++ b/src/frontend/src/app/frontcraft/pages/pages.module.ts @@ -2,7 +2,6 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; - import { NbAlertModule, NbButtonModule, @@ -10,10 +9,16 @@ import { NbInputModule, NbMenuModule, NbCardModule, - NbTreeGridModule + NbTreeGridModule, + NbListModule, + NbTabsetModule, + NbIconModule, + NbWindowModule, + NbPopoverModule, + NbDatepickerModule, + NbSelectModule } from '@nebular/theme'; import { MyAuthRoutingModule } from './pages-routing.module'; -import { FrontcraftDashboardComponent } from './dashboard/dashboard.component'; import { PagesLayoutComponent } from './pages-layout.component'; import { ThemeModule } from '../../@theme/theme.module'; import { DashboardModule } from '../../demo_pages/dashboard/dashboard.module'; @@ -21,7 +26,17 @@ import { ECommerceModule } from '../../demo_pages/e-commerce/e-commerce.module'; import { MiscellaneousModule } from '../../demo_pages/miscellaneous/miscellaneous.module'; import { FrontcraftCharacterComponent } from './character/character.component'; import { FrontcraftUserComponent } from './user/user.component'; -import { FrontcraftPeopleComponent } from './people/people.component'; +import { FrontcraftRaidsComponent } from './raids/raids.component'; +import { FrontcraftCreateRaidsComponent } from './raids/createraid.compontent'; +import { FrontcraftRaidComponent } from './raid/raid.component'; +import { FrontcraftArchiveComponent } from './raid/archive.component'; +import { NbEvaIconsModule } from '@nebular/eva-icons'; +import { FrontcraftCharacerpickerComponent } from './raid/characterpicker.component'; +import { FrontcraftShopComponent, FrontcraftBuyTokenComponent } from './shop/shop.component'; +import { FrontcraftRulesComponent } from './rules/rules.component'; +import { FrontcraftItemSelectComponent } from './shop/itemselector.component'; +import { NgxEchartsModule } from 'ngx-echarts'; +import { FrontcraftCharactersComponent } from './characters/characters.component'; @NgModule({ @@ -35,21 +50,43 @@ import { FrontcraftPeopleComponent } from './people/people.component'; NbInputModule, NbButtonModule, NbCheckboxModule, + NbTabsetModule, ThemeModule, NbMenuModule, DashboardModule, ECommerceModule, MiscellaneousModule, NbCardModule, - + NbListModule, + NbIconModule, + NbEvaIconsModule, + NbPopoverModule, + NbDatepickerModule, + NbSelectModule, + NgxEchartsModule, + NbWindowModule.forChild(), ], declarations: [ - FrontcraftPeopleComponent, + FrontcraftItemSelectComponent, + FrontcraftRaidComponent, + FrontcraftRaidsComponent, FrontcraftUserComponent, FrontcraftCharacterComponent, PagesLayoutComponent, - FrontcraftDashboardComponent + FrontcraftCharactersComponent, + FrontcraftCharacerpickerComponent, + FrontcraftArchiveComponent, + FrontcraftShopComponent, + FrontcraftRulesComponent, + FrontcraftCreateRaidsComponent, + FrontcraftBuyTokenComponent, ], + entryComponents: [ + FrontcraftItemSelectComponent, + FrontcraftCharacerpickerComponent, + FrontcraftCreateRaidsComponent, + FrontcraftBuyTokenComponent, + ] }) export class FrontcraftPagesModule { } \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/people/people.component.html b/src/frontend/src/app/frontcraft/pages/people/people.component.html deleted file mode 100644 index f37cf9b..0000000 --- a/src/frontend/src/app/frontcraft/pages/people/people.component.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -
- {{customColumn}} - - - - - {{row.data[customColumn]}} - - - {{column}} - {{row.data[column] || '-'}}
-
-
\ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/people/people.component.scss b/src/frontend/src/app/frontcraft/pages/people/people.component.scss deleted file mode 100644 index 5667cf7..0000000 --- a/src/frontend/src/app/frontcraft/pages/people/people.component.scss +++ /dev/null @@ -1,42 +0,0 @@ -button[nbTreeGridRowToggle] { - background: transparent; - border: none; - padding: 0; - } - - .search-label { - display: block; - } - .search-input { - margin-bottom: 1rem; - } - - .nb-column-name { - width: 100%; - } - - @media screen and (min-width: 400px) { - .nb-column-name, - .nb-column-size { - width: 50%; - } - } - - @media screen and (min-width: 500px) { - .nb-column-name, - .nb-column-size, - .nb-column-kind { - width: 33.333%; - } - } - - @media screen and (min-width: 600px) { - .nb-column-name { - width: 31%; - } - .nb-column-size, - .nb-column-kind, - .nb-column-items { - width: 23%; - } - } \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/people/people.component.ts b/src/frontend/src/app/frontcraft/pages/people/people.component.ts deleted file mode 100644 index 93558d7..0000000 --- a/src/frontend/src/app/frontcraft/pages/people/people.component.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Component } from '@angular/core'; -import { NbSortDirection, NbSortRequest, NbTreeGridDataSourceBuilder, NbTreeGridDataSource } from '@nebular/theme'; - -interface TreeNode { - data: T; - children?: TreeNode[]; - expanded?: boolean; -} - -interface Row { - name: string, - character: any, - SRC: any, - kind: 'Character' | 'Account' -} - -type TreeType = TreeNode - -@Component({ - selector: 'people-component', - templateUrl: './people.component.html', - styleUrls: ['./tree-grid-shared.scss', './people.component.scss'], -}) -export class FrontcraftPeopleComponent{ - customColumn = 'name'; - defaultColumns = [ 'character', 'SRC'/*, 'Profile'*/ ]; - allColumns = [ this.customColumn, ...this.defaultColumns ]; - - dataSource: NbTreeGridDataSource; - - sortColumn: string; - sortDirection: NbSortDirection = NbSortDirection.NONE; - - constructor(private dataSourceBuilder: NbTreeGridDataSourceBuilder) { - this.dataSource = this.dataSourceBuilder.create(this.data); - } - - updateSort(sortRequest: NbSortRequest): void { - this.sortColumn = sortRequest.column; - this.sortDirection = sortRequest.direction; - } - - getSortDirection(column: string): NbSortDirection { - if (this.sortColumn === column) { - return this.sortDirection; - } - return NbSortDirection.NONE; - } - - private data: TreeType[] = [ - { - data: {name: 'a', character: 2, SRC: 0, kind: 'Account'}, - children: [ - {data: {name: 'a', character: 'Warrior', SRC: 'Arms', kind: 'Character'}} - ] - } - ]; - - getShowOn(index: number) { - const minWithForMultipleColumns = 400; - const nextColumnStep = 100; - return minWithForMultipleColumns + (nextColumnStep * index); - } -} diff --git a/src/frontend/src/app/frontcraft/pages/people/tree-grid-shared.scss b/src/frontend/src/app/frontcraft/pages/people/tree-grid-shared.scss deleted file mode 100644 index e490e9a..0000000 --- a/src/frontend/src/app/frontcraft/pages/people/tree-grid-shared.scss +++ /dev/null @@ -1,10 +0,0 @@ -::ng-deep { - body { - min-height: 20rem; - } - - .nb-tree-grid-header-cell, - .nb-tree-grid-header-cell button { - text-transform: capitalize; - } - } \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/raid/archive.component.ts b/src/frontend/src/app/frontcraft/pages/raid/archive.component.ts new file mode 100644 index 0000000..2c89b0c --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/raid/archive.component.ts @@ -0,0 +1,64 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { ApiService as ApiService } from '../../services/login-api'; +import { RaidData } from '../../../../../../backend/Types/Types'; +import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; + +@Component({ + selector: 'archive', + templateUrl: './raid.component.html', +}) +export class FrontcraftArchiveComponent implements OnInit{ + + canSignup = false + isSignedup = false + canManage = false + mySignup + + raid: RaidData = { + participants:{ + Druid: [], + Hunter: [], + Mage: [], + Paladin: [], + Priest: [], + Rogue: [], + Shaman: [], + Warlock: [], + Warrior: [], + }, + tokens:{} + } + + constructor( + private api: ApiService, + private route: ActivatedRoute, + ){ + } + + async ngOnInit(){ + this.refresh() + } + + refresh = async () => { + const param = this.route.snapshot.paramMap.get('id'); + + const raidManager = this.api.get('RaidManager') + const raiddata = await raidManager.getArchiveRaid(parseInt(param)) + this.raid = raiddata + + Object.values(raiddata.participants).flat().forEach(p => { + p['color'] = getClassColor(p.class) + }) + + const matchingSignup = Object.values(raiddata.participants).flat().find(char => char.userid === this.api.getCurrentUser()!.id!) + if(matchingSignup){ + this.isSignedup = true + this.mySignup = matchingSignup + this.mySignup.status = matchingSignup['benched']?'Bench':matchingSignup['late']?'Late':'Attending' + }else{ + this.isSignedup = false + this.mySignup = null + } + } +} \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.html b/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.html new file mode 100644 index 0000000..a7dbfbc --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.html @@ -0,0 +1,30 @@ + + + Pick Character + + +
+ + + + + {{character.charactername}} + + +
+
+
\ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.ts b/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.ts new file mode 100644 index 0000000..c4c266e --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.ts @@ -0,0 +1,44 @@ +import { OnInit, Component } from '@angular/core'; +import { NbWindowRef, NbToastrService, NbDialogRef } from '@nebular/theme'; +import { RaidData, Character } from '../../../../../../backend/Types/Types'; +import { ApiService } from '../../services/login-api'; +import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; + +@Component({ + selector: 'characterpicker', + templateUrl: './characterpicker.component.html', +}) +export class FrontcraftCharacerpickerComponent implements OnInit{ + + raid : RaidData + characters: Character[] + + constructor( + protected dialogRef: NbDialogRef, + private api : ApiService, + private toast : NbToastrService + ){} + + signup = async (character: Character, late: boolean) => { + const auth = this.api.getAuth() + const signup = this.api.get('signup') + if(!signup) return + + await signup.sign(auth.token.value, character, this.raid, late) + this.toast.show('Signup', 'Success', { status: 'success' }) + this.dialogRef.close() + } + + ngOnInit(): void { + const usr = this.api.getCurrentUser() + this.api.get('CharacterManager') + .getCharactersOfUser(usr.username) + .then(chars => { + chars.forEach(char => { + char['color'] = getClassColor(char.class) + }) + this.characters = chars + }) + } + +} \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/raid/raid.component.html b/src/frontend/src/app/frontcraft/pages/raid/raid.component.html new file mode 100644 index 0000000..2a89726 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/raid/raid.component.html @@ -0,0 +1,119 @@ + + + + +

{{raid.title}}

+

+ {{raid.signupcount}} / {{raid.size}} signups +

+

+ {{raid.description}} +

+
+
+ +

+ You are signed as: {{mySignup.charactername}} ({{mySignup.race}} {{mySignup.specname}} {{mySignup.class}})
+ Status: {{mySignup.status}} +

+ + +
+
+ +
+
+
+ + +
+ + + {{group.key}} ({{group.value.length}}) + + + + + + +
+
+ + + + + + +   + {{item.key}} +
+
+
+ [ {{token.level}} ] + + {{token.charactername}} +
+
+
+
+
+
+ + + + + +
+
+
+ diff --git a/src/frontend/src/app/frontcraft/pages/raid/raid.component.ts b/src/frontend/src/app/frontcraft/pages/raid/raid.component.ts new file mode 100644 index 0000000..6b21fc8 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/raid/raid.component.ts @@ -0,0 +1,123 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ApiService as ApiService } from '../../services/login-api'; +import { RaidData, Raid, Signup } from '../../../../../../backend/Types/Types'; +import { NbWindowService, NbToastrService, NbDialogService } from '@nebular/theme'; +import { FrontcraftCharacerpickerComponent } from './characterpicker.component'; +import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; + +@Component({ + selector: 'raid', + templateUrl: './raid.component.html', +}) +export class FrontcraftRaidComponent implements OnInit{ + + canSignup = false + isSignedup = false + manageRaid + mySignup + + raid: RaidData = { + participants:{ + Druid: [], + Hunter: [], + Mage: [], + Paladin: [], + Priest: [], + Rogue: [], + Shaman: [], + Warlock: [], + Warrior: [], + }, + tokens:{} + } + + constructor( + private api: ApiService, + private route: ActivatedRoute, + private router: Router, + private dialogService : NbDialogService, + private toast: NbToastrService + ){ + window['r'] = this + } + + async ngOnInit(){ + this.manageRaid = this.api.get('manageRaid') + + const signupFeature = this.api.get('signup') + if(signupFeature){ + this.canSignup = true + } + this.refresh() + } + + signup = async () => { + const signupFeature = this.api.get('signup') + if(!signupFeature) return + + this.dialogService.open(FrontcraftCharacerpickerComponent, { + closeOnBackdropClick: true, + closeOnEsc: true, + context: { + 'raid': this.raid, + } + }).onClose.subscribe(()=>{ + this.refresh() + }); + } + + async archiveRaid(raid:Raid){ + await this.manageRaid!.archiveRaid(raid) + this.toast.show('Raid archived', 'Success', { status: 'success' }) + this.router.navigateByUrl('/frontcraft/archive/'+raid.id) + } + + async startRaid(raid:Raid){ + await this.manageRaid!.startRaid(raid) + this.toast.show('Raid started', 'Success', { status: 'success' }) + this.router.navigateByUrl('/frontcraft/archive/'+raid.id) + } + + unsign = async () => { + const signupFeature = this.api.get('signup') + if(!signupFeature) return + + await signupFeature.unsign(this.api.getAuth().token.value, this.mySignup, this.raid) + this.toast.show('Success', 'Unsigned', { status: 'success' }) + this.refresh() + } + + refresh = async () => { + const param = this.route.snapshot.paramMap.get('id'); + + const raidManager = this.api.get('RaidManager') + const raiddata = await raidManager.getRaidData({ + id: param + }) + this.raid = raiddata + + Object.values(raiddata.participants).flat().forEach(p => { + p['color'] = getClassColor(p.class) + }) + const user = this.api.getCurrentUser() + const matchingSignup = Object.values(raiddata.participants).flat().find(char => user && char.userid === user.id!) + if(matchingSignup){ + this.isSignedup = true + this.mySignup = matchingSignup + this.mySignup.status = matchingSignup['benched']?'Bench':matchingSignup['late']?'Late':'Attending' + }else{ + this.isSignedup = false + this.mySignup = null + } + } + + setBench(signup:Signup){ + this.manageRaid.setBenched({ + characterid: signup.characterid, + raidid: signup.raidid, + late: false, + benched: !signup.benched + }).then(_ => this.refresh()) + } +} \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/raids/createraid.component.html b/src/frontend/src/app/frontcraft/pages/raids/createraid.component.html new file mode 100644 index 0000000..0a161f0 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/raids/createraid.component.html @@ -0,0 +1,28 @@ + + + + + Create Raid + + + {{template.title}} {{template.start| date : 'd MMMM'}} + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/raids/createraid.compontent.ts b/src/frontend/src/app/frontcraft/pages/raids/createraid.compontent.ts new file mode 100644 index 0000000..79b7756 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/raids/createraid.compontent.ts @@ -0,0 +1,59 @@ +import { Component, OnInit } from '@angular/core'; +import { ApiService } from '../../services/login-api'; +import { Raid, RaidData } from '../../../../../../backend/Types/Types'; +import { NbWindowRef, NbDialogRef } from '@nebular/theme'; + +const ONE_MINUTE = 60000 +const ONE_HOUR = 60 * ONE_MINUTE + +@Component({ + selector: 'createraid', + templateUrl: 'createraid.component.html', +}) +export class FrontcraftCreateRaidsComponent implements OnInit { + + templates :RaidData[] + template :RaidData + + title = "" + startdate = new Date() + hour = 20 + minute = 0 + size = 40 + description = "" + + constructor( + protected dialogRef: NbDialogRef, + private api: ApiService + ) {} + + loadNext(cardData) { + } + + onTemplateSelect(){ + const templateDate = new Date(this.template.start) + this.title = this.template.title + this.hour = templateDate.getHours() + this.minute = templateDate.getMinutes() + this.description = this.template.description + this.startdate = new Date(parseInt(this.template.start) - templateDate.getHours()*ONE_HOUR - templateDate.getMinutes()*ONE_MINUTE) + } + + ngOnInit(){ + + } + + async submit(){ + const raid = { + description: this.description, + size: this.size, + start: ""+new Date(this.startdate.getTime() + this.hour*ONE_HOUR + this.minute*ONE_MINUTE).getTime(), + title: this.title, + } + const manage = this.api.get('manageRaid') + if(!manage) return + + await manage.createRaid(raid) + this.dialogRef.close() + } +} diff --git a/src/frontend/src/app/frontcraft/pages/raids/raids.component.html b/src/frontend/src/app/frontcraft/pages/raids/raids.component.html new file mode 100644 index 0000000..bea8bd5 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/raids/raids.component.html @@ -0,0 +1,37 @@ + + + Upcoming raids + + + + + {{raid.title}}
+ {{raid.signupcount}} / {{raid.size}}
+ {{raid.start | date : 'EEEE d MMMM @ HH:mm'}}
+ {{raid.description}}
+
+
+
+ + + Previous raids + + + {{raid.title}}
+ {{raid.signupcount}} / {{raid.size}}
+ {{raid.start | date : 'EEEE d MMMM @ HH:mm'}}
+ {{raid.description}}
+
+
+
+ \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/raids/raids.component.scss b/src/frontend/src/app/frontcraft/pages/raids/raids.component.scss new file mode 100644 index 0000000..0e5b7ee --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/raids/raids.component.scss @@ -0,0 +1,7 @@ +.infinite-cards { + nb-card { + &.own-scroll { + height: 50vh; + } + } +} diff --git a/src/frontend/src/app/frontcraft/pages/raids/raids.component.ts b/src/frontend/src/app/frontcraft/pages/raids/raids.component.ts new file mode 100644 index 0000000..f40b133 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/raids/raids.component.ts @@ -0,0 +1,54 @@ +import { Component, OnInit } from '@angular/core'; +import { ApiService } from '../../services/login-api'; +import { NbWindowService, NbDialogService } from '@nebular/theme'; +import { FrontcraftCreateRaidsComponent } from './createraid.compontent'; + +@Component({ + selector: 'raids', + templateUrl: 'raids.component.html', + styleUrls: ['raids.component.scss'], +}) +export class FrontcraftRaidsComponent implements OnInit{ + + + manageRaid + raids = [] + oldraids = [] + pageSize = 10; + + constructor( + private api: ApiService, + private dialogService: NbDialogService + ) { + this.manageRaid = this.api.get('manageRaid') + + } + + ngOnInit(): void { + this.refresh() + } + + refresh() { + this.api.get('RaidManager').getRaids().then(raids => { + this.raids = raids + }) + + const raidManager = this.api.get('RaidManager') + raidManager.getPastRaids(10).then(raiddata => { + this.oldraids = raiddata + }) + } + + create(){ + this.dialogService.open(FrontcraftCreateRaidsComponent, { + closeOnBackdropClick: true, + closeOnEsc: true, + context: { + templates: this.oldraids + } + }).onClose.subscribe(()=>{ + this.refresh() + }); + + } +} diff --git a/src/frontend/src/app/frontcraft/pages/rules/rules.component.html b/src/frontend/src/app/frontcraft/pages/rules/rules.component.html new file mode 100644 index 0000000..caed67f --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/rules/rules.component.html @@ -0,0 +1,31 @@ + + + + + Smart text here + + + + + + +   + {{item.key}} +
+ + + + {{rule.description}} + + +{{rule.modifier}} {{rule.race}} {{rule.specname}} {{rule.class}}
+
+
+
+
+
+
+
+ diff --git a/src/frontend/src/app/frontcraft/pages/rules/rules.component.ts b/src/frontend/src/app/frontcraft/pages/rules/rules.component.ts new file mode 100644 index 0000000..cdc0afc --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/rules/rules.component.ts @@ -0,0 +1,31 @@ +import { Component, OnInit } from '@angular/core'; +import { ApiService as ApiService } from '../../services/login-api'; +import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; + +@Component({ + selector: 'rules', + templateUrl: './rules.component.html', +}) +export class FrontcraftRulesComponent implements OnInit{ + + managePriorities + rules = {} + + constructor( + private api: ApiService, + ){ + } + + async ngOnInit(){ + this.api.get('ItemManager').getAllPriorities().then(prios => prios.forEach(prio => { + if(!this.rules[prio.itemname]){ + this.rules[prio.itemname] = [] + } + prio['color'] = getClassColor(prio.class) || '#FFFFFF' + this.rules[prio.itemname].push(prio) + })) + + const managePriorities = this.api.get('managePriorities') + this.managePriorities = managePriorities + } +} \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/shop/itemselect.component.html b/src/frontend/src/app/frontcraft/pages/shop/itemselect.component.html new file mode 100644 index 0000000..150d7ee --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/shop/itemselect.component.html @@ -0,0 +1,26 @@ + + + + + + +   + + + +   + {{item.itemname}} + + + + diff --git a/src/frontend/src/app/frontcraft/pages/shop/itemselector.component.ts b/src/frontend/src/app/frontcraft/pages/shop/itemselector.component.ts new file mode 100644 index 0000000..f2ba204 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/shop/itemselector.component.ts @@ -0,0 +1,45 @@ +import { Component, OnInit } from '@angular/core'; +import { ApiService } from '../../services/login-api'; +import { Item } from '../../../../../../backend/Types/Types'; + +@Component({ + selector: 'itemselect', + templateUrl: './itemselect.component.html', +}) +export class FrontcraftItemSelectComponent implements OnInit{ + + selected: Item + search: string + items: any[] + displayedItems: any[] + + callbacks = [] + + constructor( + private api: ApiService, + ){ + } + + async ngOnInit(){ + this.api.get('ItemManager').getItems().then(items => { + this.items = items + this.displayedItems = items + }) + } + + changeSearch(){ + if(!this.search || this.search == "") + this.displayedItems = this.items + else + this.displayedItems = this.items.filter(it => it.itemname.toLowerCase().includes(this.search.toLowerCase())) + } + + select(item: Item){ + this.selected = item + this.callbacks.forEach(cb => cb(item)) + } + + onselect(callback:Function){ + this.callbacks.push(callback) + } +} \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/shop/shop.component.html b/src/frontend/src/app/frontcraft/pages/shop/shop.component.html new file mode 100644 index 0000000..f0b54c4 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/shop/shop.component.html @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/frontend/src/app/frontcraft/pages/shop/shop.component.ts b/src/frontend/src/app/frontcraft/pages/shop/shop.component.ts new file mode 100644 index 0000000..ac48f52 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/shop/shop.component.ts @@ -0,0 +1,135 @@ +import { Component, OnInit, ContentChild, AfterContentInit, ViewChild, AfterViewInit } from '@angular/core'; +import { ApiService as ApiService } from '../../services/login-api'; +import { FrontcraftItemSelectComponent } from './itemselector.component'; +import { NbWindowService, NbWindowRef, NbToastrService, NbDialogService, NbDialogRef } from '@nebular/theme'; +import { Item, Character, SRToken } from '../../../../../../backend/Types/Types'; +import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; + +@Component({ + selector: 'shop', + templateUrl: './shop.component.html', +}) +export class FrontcraftShopComponent implements AfterViewInit{ + + @ViewChild(FrontcraftItemSelectComponent, {static: false}) + itemselect !: FrontcraftItemSelectComponent + + constructor( + private api: ApiService, + private dialogService : NbDialogService + + ){ + window['shop'] = this + } + + buy = (item) => { + + this.dialogService.open(FrontcraftBuyTokenComponent, { + closeOnBackdropClick: true, + closeOnEsc: true, + context: { + item: item + } + }).onClose.subscribe(()=>{ + }); + + } + + ngAfterViewInit(){ + this.itemselect.onselect(this.buy) + } +} + +@Component({ + selector: 'buyToken', + template: ` + + + Buy {{item.itemname}} + + +

+ You currently have {{currency}} softreserve currency +

+
+
+ + {{kv.key}} (+{{kv.value}} from priorities) +
+
+ + {{token.charactername}} [ {{token.level}} ] => [ {{token.level+1}} ] +
+
+
+
+ + `, +}) +export class FrontcraftBuyTokenComponent implements OnInit{ + + item : Item + characters: Character[] + modifier = {} + currency: number = 0 + ownedtokens: SRToken[] = [] + + constructor( + private toastr: NbToastrService, + protected dialogRef: NbDialogRef, + private api : ApiService + ){} + + ngOnInit(): void { + const usr = this.api.getCurrentUser() + this.api.get('CharacterManager') + .getCharactersOfUser(usr.username) + .then(chars => { + chars.forEach(char => { + char['color'] = getClassColor(char.class) + this.api.get('ItemManager').getToken(char, this.item).then(token => { + if(token) this.ownedtokens.push(token) + else{ + this.api.get('ItemManager').calculatePriorities(this.item.itemname, char).then(modifier => { + this.modifier[char.charactername] = modifier + }) + } + }) + }) + this.characters = chars + }) + this.api.get('UserManager').getUser(usr.username).then(u => { + if(!u) return + this.currency = u.currency + }) + } + + buyToken = async (charactername:string) => { + + const src = this.api.get('ItemManager') + const token = await src.buyToken(this.api.getAuth().token.value, charactername, this.item.itemname) + + + if(token){ + this.toastr.show(token.characterid+' now has a token for '+token.itemname+' of level '+token.level, 'Yay', {status: 'success'}) + }else{ + this.toastr.show('Error (something went wrong)', 'Oh no', {status: 'danger'}) + } + this.dialogRef.close() + } +} \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/user/user.component.html b/src/frontend/src/app/frontcraft/pages/user/user.component.html index b4aa189..580598b 100644 --- a/src/frontend/src/app/frontcraft/pages/user/user.component.html +++ b/src/frontend/src/app/frontcraft/pages/user/user.component.html @@ -1,11 +1,12 @@ -

{{user.username}}

+ {{user.currency}} @@ -17,7 +18,20 @@ accent="info"> - {{char.specname}} {{char.class}} + {{char.race}}
+ {{char.specname}} {{char.class}} +
+
+ + [ {{token.level}} ] + + + {{token.itemname}} +
+
diff --git a/src/frontend/src/app/frontcraft/pages/user/user.component.ts b/src/frontend/src/app/frontcraft/pages/user/user.component.ts index 08915b6..267c237 100644 --- a/src/frontend/src/app/frontcraft/pages/user/user.component.ts +++ b/src/frontend/src/app/frontcraft/pages/user/user.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; -import { LoginApiService } from '../../services/login-api'; -import { Router } from '@angular/router'; +import { ApiService } from '../../services/login-api'; +import { Router, ActivatedRoute } from '@angular/router'; import { User, Spec, Character } from '../../../../../../backend/Types/Types'; import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; @@ -14,25 +14,24 @@ export class FrontcraftUserComponent implements OnInit{ characters : (Character & Spec)[] = [] constructor( - private router : Router, - private loginApi : LoginApiService + private route: ActivatedRoute, + private api : ApiService ){} - ngOnInit(){ - const auth = this.loginApi.getAuth() - if(!auth){ - this.router.navigateByUrl('/auth/login') - return - } - this.user = auth.user - this.loginApi.getUnprivilegedSocket() - .CharacterManager - .getCharactersOfUser(this.user.username) - .then((characters) => { - characters.forEach(c => { - c['color'] = getClassColor(c.class) - }) - this.characters = characters - }) + async ngOnInit(){ + const param = this.route.snapshot.paramMap.get('name'); + const usr = await this.api.get('UserManager').getUser(param) + + + if(!usr) return + this.user = usr + + const characters = await this.api.get('CharacterManager').getCharactersOfUser(this.user.username) + characters.forEach(async c => { + const tokens = await this.api.get('ItemManager').getTokens(c) + c['tokens'] = tokens + c['color'] = getClassColor(c.class) + }) + this.characters = characters } } \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/services/login-api.ts b/src/frontend/src/app/frontcraft/services/login-api.ts index a912218..c87adb0 100644 --- a/src/frontend/src/app/frontcraft/services/login-api.ts +++ b/src/frontend/src/app/frontcraft/services/login-api.ts @@ -1,12 +1,13 @@ import { Injectable, Injector, NgZone } from "@angular/core"; import {RPCSocket} from 'rpclibrary/js/src/Frontend' -import { Token, Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../backend/Types/Types' +import { Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../backend/Types/Types' import { CookieService } from 'ngx-cookie-service'; import { Router } from '@angular/router'; +import { ShoutMessage } from '../../../../../backend/Components/Shoutbox/Interface'; @Injectable() -export class LoginApiService{ +export class ApiService{ private socket:RPCSocket & FrontcraftIfc; private auth:Auth private privSocket: RPCSocket & SomeOf @@ -37,7 +38,7 @@ export class LoginApiService{ sock.hook('getUserData', () => auth) sock.hook('navigate', (where:string) => { this.ngZone.run( () => { - this.injector.get(Router).navigateByUrl('/') + this.injector.get(Router).navigateByUrl(where) }) }) sock.on('error', (e) => { @@ -55,13 +56,17 @@ export class LoginApiService{ } } - getFeature = async (feature : K) : Promise => { + get = (feature : K) : K extends keyof FrontcraftIfc?FrontcraftIfc[K]: + K extends keyof FrontcraftFeatureIfc?(FrontcraftFeatureIfc[K] | void): + never => { try{ - const sock = await this.getPrivilegedSocket(this.auth) - if(sock[feature]) return sock[feature] + //@ts-ignore + if(this.socket && this.socket[feature]) return this.socket[feature] + //@ts-ignore + if(this.privSocket && this.privSocket[feature]) return this.privSocket[feature] }catch(e){ return - } + } } getCurrentUser = () : User | undefined => { @@ -70,7 +75,7 @@ export class LoginApiService{ login = async (username: string, password: string) : Promise> => { const pwHash = await hash(password) - const auth = await this.socket.Authenticator.login(username, pwHash) + const auth = await this.socket.UserManager.login(username, pwHash) if(!auth){ await this.logout() @@ -80,9 +85,14 @@ export class LoginApiService{ return sock } + async connectShoutbox(handler) : Promise{ + const res = await this.get('Shoutbox').subscribe(handler) + return async (msg: ShoutMessage) => await this.get('Shoutbox').shout(res.uuid, msg) + } + logout = async () => { this.cookieSvc.set('token', undefined) - if(this.auth) await this.socket.Authenticator.logout(this.auth.user.username, this.auth.token.value) + if(this.auth) await this.socket.UserManager.logout(this.auth.user.username, this.auth.token.value) if(this.privSocket) this.privSocket.destroy() this.privSocket = null this.auth = null @@ -91,15 +101,16 @@ export class LoginApiService{ }) } - initialize = async () : Promise => { const sock = await RPCSocket.makeSocket(20000, window.location.hostname) + this.socket = sock try{ const cookie = JSON.parse(this.cookieSvc.get('token')) + if(cookie != null) { try{ - const auth = await sock.Authenticator.getAuth(cookie.token.value) + const auth = await sock.UserManager.getAuth(cookie.token.value) if(!auth) return sock return await this.getPrivilegedSocket(auth) }catch(e){ @@ -115,7 +126,7 @@ export class LoginApiService{ async checkLogin() : Promise{ if(!this.auth) return false - const valid = await this.socket.Authenticator.checkToken(this.auth.token.value, this.auth.user.rank) + const valid = this.socket.UserManager.checkToken(this.auth.token.value, this.auth.user.rank) if(valid) return true await this.logout() return false @@ -126,7 +137,7 @@ function str2arraybuf(str:string): ArrayBuffer { return new Buffer(str) } -function buf2hex(buffer) { // buffer is an ArrayBuffer +function buf2hex(buffer: ArrayBuffer) { return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join(''); } @@ -137,6 +148,6 @@ export async function hash(value:string) : Promise{ } //angular depenency manager requires this -export function initializeLoginSvc(svc: LoginApiService): () => Promise { - return svc.initialize +export function initializeLoginSvc(svc: ApiService): () => Promise { + return ()=>svc.initialize?svc.initialize():undefined } \ No newline at end of file diff --git a/src/frontend/tsconfig.json b/src/frontend/tsconfig.json index 9128517..370415c 100644 --- a/src/frontend/tsconfig.json +++ b/src/frontend/tsconfig.json @@ -2,7 +2,7 @@ "compileOnSave": false, "compilerOptions": { "importHelpers": true, - "module": "esnext", + "module": "commonjs", "outDir": "./dist/out-tsc", "sourceMap": true, "declaration": false,