From f7f770a40c41da91e2660aa92cc603db994d2fb8 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 30 Jan 2020 15:22:05 +0100 Subject: [PATCH] safety commit before system upgrade --- .../Components/Character/CharacterManager.ts | 59 +++++++++---- src/backend/Components/Character/Interface.ts | 4 +- .../Components/Character/RPCInterface.ts | 2 + src/backend/Components/Item/ItemManager.ts | 17 ++-- src/backend/Components/Login/Interface.ts | 5 +- src/backend/Components/Login/LoginManager.ts | 84 +++++++++++-------- src/backend/Components/Raid/Interface.ts | 4 +- src/backend/Components/Raid/RaidManager.ts | 50 +++++++---- src/backend/Injector/Injector.ts | 6 +- src/backend/Injector/ServiceDecorator.ts | 6 +- src/backend/Types/PlayerSpecs.ts | 18 +++- src/backend/Types/Types.ts | 21 +++-- src/frontend/package-lock.json | 6 +- src/frontend/package.json | 2 +- .../components/header/header.component.html | 2 +- .../components/header/header.component.ts | 4 +- src/frontend/src/app/app-routing.module.ts | 1 + src/frontend/src/app/app.module.ts | 1 - .../auth/register/register.component.html | 2 +- .../auth/register/register.component.ts | 19 ++++- .../pages/character/character.component.html | 10 +++ .../pages/character/character.component.ts | 34 ++++++++ .../pages/dashboard/dashboard.component.html | 36 +++++++- .../pages/dashboard/dashboard.component.scss | 42 ++++++++++ .../pages/dashboard/dashboard.component.ts | 61 ++++++++++++-- .../pages/dashboard/tree-grid-shared.scss | 10 +++ .../pages/pages-layout.component.ts | 11 ++- .../frontcraft/pages/pages-routing.module.ts | 15 ++++ .../src/app/frontcraft/pages/pages.module.ts | 10 ++- .../pages/people/people.component.html | 36 ++++++++ .../pages/people/people.component.scss | 42 ++++++++++ .../pages/people/people.component.ts | 64 ++++++++++++++ .../pages/people/tree-grid-shared.scss | 10 +++ .../frontcraft/pages/user/user.component.html | 24 ++++++ .../frontcraft/pages/user/user.component.ts | 38 +++++++++ .../src/app/frontcraft/services/login-api.ts | 20 +++-- 36 files changed, 651 insertions(+), 125 deletions(-) create mode 100644 src/frontend/src/app/frontcraft/pages/character/character.component.html create mode 100644 src/frontend/src/app/frontcraft/pages/character/character.component.ts create mode 100644 src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.scss create mode 100644 src/frontend/src/app/frontcraft/pages/dashboard/tree-grid-shared.scss create mode 100644 src/frontend/src/app/frontcraft/pages/people/people.component.html create mode 100644 src/frontend/src/app/frontcraft/pages/people/people.component.scss create mode 100644 src/frontend/src/app/frontcraft/pages/people/people.component.ts create mode 100644 src/frontend/src/app/frontcraft/pages/people/tree-grid-shared.scss create mode 100644 src/frontend/src/app/frontcraft/pages/user/user.component.html create mode 100644 src/frontend/src/app/frontcraft/pages/user/user.component.ts diff --git a/src/backend/Components/Character/CharacterManager.ts b/src/backend/Components/Character/CharacterManager.ts index 2371004..ffdb88f 100644 --- a/src/backend/Components/Character/CharacterManager.ts +++ b/src/backend/Components/Character/CharacterManager.ts @@ -1,6 +1,6 @@ import { RPCInterface } from "rpclibrary"; import { Inject, Module } from "../../Injector/ServiceDecorator"; -import { TableDefiniton, Character } from "../../Types/Types"; +import { TableDefiniton, Character, Spec, User } from "../../Types/Types"; import { CharacterManagerIfc } from "./RPCInterface"; import { FrontworkComponent } from "../../Types/FrontworkComponent"; import { getSpecTableData, SpecT } from "../../Types/PlayerSpecs"; @@ -18,12 +18,21 @@ implements FrontworkComponent, ICharacterMana private admin: IAdmin @Inject(ILoginManager) - private loginManager : any + private loginManager : ILoginManager exportRPCs = () => [ { - name: 'getSpecId' as 'getSpecId', - call: this.getSpecId + 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 } ] @@ -44,7 +53,7 @@ implements FrontworkComponent, ICharacterMana name: 'characters', tableBuilder: (table) => { table.increments("id").primary() - table.string("name").notNullable().unique() + table.string("charactername").notNullable().unique() table.integer("specid").notNullable() table.foreign("specid").references("specs.id") table.integer("userid").notNullable() @@ -55,8 +64,8 @@ implements FrontworkComponent, ICharacterMana tableBuilder: (table) => { table.increments("id") table.string('class') - table.string('name') - table.unique(['class', 'name']) + table.string('specname') + table.unique(['class', 'specname']) } } ] @@ -64,17 +73,14 @@ implements FrontworkComponent, ICharacterMana private initialized = false initialize = async () => { - if(!this.initialized) - this.initialized = true - //initialize spec table - - getLogger('CharacterManager').debug('inserting specs') - + if(this.initialized) return + this.initialized = true await this.admin.knex('specs').insert(getSpecTableData()).catch(e => { console.log("skipping spec insertion") }) } createCharacter = async (userToken: string, character : Character) : Promise => { try{ + character.charactername = character.charactername.toLowerCase() const user = this.loginManager.getUserRecordByToken(userToken) await this.admin.knex('characters').insert(character) const char : Character = await this.admin.knex.select('*').from('characters').where(character).first() @@ -90,12 +96,33 @@ implements FrontworkComponent, ICharacterMana return this.admin.knex.select('*').from('characters') } + getCharacterByName = async(charactername: string) : Promise<(Character & User & Spec) | void> => { + charactername = charactername.toLowerCase() + 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', ) + .where('charactername', '=', charactername) + .first() + } + + getCharactersOfUser = async(username: string) : Promise<(Character & Spec)[]> => { + username = username.toLowerCase() + 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') + .where('u.username', '=', username) + } + getSpecId = async (clazz: c, name: SpecT[c]) => await this.admin.knex .from('specs') .select('id') - .where({ + .where({ class: clazz, - name: name - }).first().then(spec => spec.id) + specname: name + }) + .first() + .then(spec => spec.id) } \ No newline at end of file diff --git a/src/backend/Components/Character/Interface.ts b/src/backend/Components/Character/Interface.ts index b601621..f46981a 100644 --- a/src/backend/Components/Character/Interface.ts +++ b/src/backend/Components/Character/Interface.ts @@ -1,4 +1,4 @@ -import { Character } from "../../Types/Types" +import { Character, Spec, User } from "../../Types/Types" import { SpecT } from "../../Types/PlayerSpecs" @@ -6,4 +6,6 @@ export class ICharacterManager{ createCharacter: (usertoken: string, char : Character) => Promise getSpecId: (clazz: c, name: SpecT[c]) => Promise getCharacters: () => Promise + getCharacterByName: (charactername: string) => Promise<(Character & User & Spec) | void> + getCharactersOfUser: (username: string) => Promise<(Character & Spec)[]> } \ No newline at end of file diff --git a/src/backend/Components/Character/RPCInterface.ts b/src/backend/Components/Character/RPCInterface.ts index 606421f..55f7908 100644 --- a/src/backend/Components/Character/RPCInterface.ts +++ b/src/backend/Components/Character/RPCInterface.ts @@ -5,6 +5,8 @@ export type CharacterManagerIfc = { CharacterManager: { getSpecId : ICharacterManager['getSpecId'] getCharacters : ICharacterManager['getCharacters'] + getCharacterByName : ICharacterManager['getCharacterByName'] + getCharactersOfUser: ICharacterManager['getCharactersOfUser'] } } diff --git a/src/backend/Components/Item/ItemManager.ts b/src/backend/Components/Item/ItemManager.ts index b833034..09757f5 100644 --- a/src/backend/Components/Item/ItemManager.ts +++ b/src/backend/Components/Item/ItemManager.ts @@ -44,7 +44,7 @@ implements FrontworkComponent, TableDefin try{ return { - name: r.wowhead.item[0].name[0], + itemname: r.wowhead.item[0].name[0], iconname: r.wowhead.item[0].icon[0]._, url: r.wowhead.item[0].link[0], quality: r.wowhead.item[0].quality[0]._, @@ -58,20 +58,19 @@ implements FrontworkComponent, TableDefin getTableDefinitions(): TableDefiniton[] { return [ { - name: 'reservations', + name: 'tokens', tableBuilder: (table) => { - table.integer("user_id").primary() - table.foreign("user_id").references("id").inTable('users') - table.integer("item_id").primary() - table.foreign("item_id").references("id").inTable('items') - table.integer("raid_id").primary() - table.foreign("raid_id").references("id").inTable('raids') + table.integer("characterid").primary() + table.foreign("characterid").references("id").inTable('characters') + table.integer("itemid").primary() + table.foreign("itemid").references("id").inTable('items') + table.integer("level") } },{ name: 'items', tableBuilder: (table) => { table.increments("id").primary() - table.string('name').unique().notNullable() + table.string('itemname').unique().notNullable() table.string('iconname').notNullable() table.string('url').notNullable() table.string('quality').defaultTo('Epic').notNullable() diff --git a/src/backend/Components/Login/Interface.ts b/src/backend/Components/Login/Interface.ts index 7614db6..e4b9f72 100644 --- a/src/backend/Components/Login/Interface.ts +++ b/src/backend/Components/Login/Interface.ts @@ -1,11 +1,12 @@ -import { Auth, Rank, User, RPCPermission } from "../../Types/Types" +import { Auth, Rank, User, RPCPermission, UserRecord } from "../../Types/Types" export class ILoginManager{ login: (username:string, pwHash:string) => Promise logout: (username: string, tokenValue :string) => Promise - getAuth: (tokenValue: string) => Promise + getAuth: (tokenValue: string) => Promise createUser: (user:User) => Promise setPermission: (perm: RPCPermission) => Promise getPermissions: () => Promise checkToken: (token: string, rank: Rank) => boolean + getUserRecordByToken: (tokenValue: string) => UserRecord | void } \ No newline at end of file diff --git a/src/backend/Components/Login/LoginManager.ts b/src/backend/Components/Login/LoginManager.ts index 248659e..1757046 100644 --- a/src/backend/Components/Login/LoginManager.ts +++ b/src/backend/Components/Login/LoginManager.ts @@ -7,10 +7,10 @@ import { RaidManager } from "../Raid/RaidManager"; import { CharacterManager } from "../Character/CharacterManager"; import { LoginManagerIfc, LoginManagerFeatureIfc } from "./RPCInterface"; import { FrontworkComponent } from "../../Types/FrontworkComponent"; -import { Rank, User, Auth, _Rank, TableDefiniton, RPCPermission, FrontcraftFeatureIfc, AnyRPCExporter, Token } from "../../Types/Types"; +import { Rank, User, Auth, _Rank, TableDefiniton, RPCPermission, FrontcraftFeatureIfc, AnyRPCExporter, Token, UserRecord } from "../../Types/Types"; import { IAdmin } from "../../Admin/Interface"; import { ILoginManager } from "./Interface"; -import { getLogger } from "log4js"; +import { getLogger, Logger } from "log4js"; const uuid = require('uuid/v4') @@ -45,11 +45,7 @@ implements FrontworkComponent, ILoginMa exporters :any[] = [] rankServers : {[rank in Rank] : Serverstate} - userLogins : {[username in string] : { - user: User - connections: {[port in number]: Socket} - auth: Auth - }} = {} + userLogins : {[username in string] : UserRecord} = {} exportRPCs = () => [ { @@ -89,7 +85,7 @@ implements FrontworkComponent, ILoginMa name: 'users', tableBuilder: (table) => { table.increments("id").primary() - table.string("name").notNullable().unique() + table.string("username").notNullable().unique() table.string("pwhash").notNullable() table.string("rank").notNullable() table.string("email").nullable().unique() @@ -98,7 +94,7 @@ implements FrontworkComponent, ILoginMa },{ name: 'rpcpermissions', tableBuilder: (table) => { - table.string("name").primary().notNullable() + table.string("rpcname").primary().notNullable() _Rank.forEach(r => { if(r === 'ADMIN') table.boolean(r).defaultTo(true).notNullable() @@ -118,7 +114,7 @@ implements FrontworkComponent, ILoginMa await Promise.all( [this, ...this.exporters].flatMap(exp => exp.exportRPCFeatures().map(async (feature) => { try{ - await this.admin.knex.insert({ name: feature.name }).into('rpcpermissions') + await this.admin.knex.insert({ rpcname: feature.name }).into('rpcpermissions') }catch(e){ console.log(e); } @@ -163,32 +159,37 @@ implements FrontworkComponent, ILoginMa Object.values(this.userLogins).map(userLogin => { const auth = userLogin.auth if(!this.checkToken(auth.token.value, auth.user.rank)){ - this.logout(auth.user.name, auth.token.value) + this.logout(auth.user.username, auth.token.value) } }) } checkConnection = async (socket: Socket) => { + let data : any let tries = 0 while(!data){ tries ++ if(tries === 5){ getLogger('LoginManager').debug('Connection check failed for connection *'+socket.port) - socket.destroy() - return + return false } - data = await Promise.race([socket.call('getUserData'), new Promise((res, rej) => { setTimeout(res, 250);})]) - } - this.userLogins[data.user.name].connections[socket.port] = socket + data = await Promise.race([socket.call('getUserData'), new Promise((res, rej) => { setTimeout(res, 1000);})]) + } + if(!this.userLogins[data.user.username]){ + await this.logout(data.user.username, data.token.value) + return false + } + + this.userLogins[data.user.username].connections[socket.port] = socket await socket.call('navigate', ["/frontcraft/dashboard"]) return true } setPermission = async (permission: RPCPermission) => { await this.admin.knex('rpcpermissions') - .where('rpcname', '=', permission.name) + .where('rpcname', '=', permission.rpcnamename) .update(permission) } @@ -200,7 +201,7 @@ implements FrontworkComponent, ILoginMa const perm : RPCPermission[] = await this.admin.knex .select(rank) .from('rpcpermissions') - .where('name', '=', feature) + .where('rpcname', '=', feature) if(perm.length === 0) return false return perm[0][rank] @@ -233,6 +234,8 @@ implements FrontworkComponent, ILoginMa user.locked = false } + user.username = user.username.toLowerCase() + await this.admin.knex('users') .insert(user) @@ -246,10 +249,20 @@ implements FrontworkComponent, ILoginMa 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 + } - await Promise.all (Object.values(this.userLogins[username].connections).map(async (sock) => { - await sock.call('navigate', '/auth/login') - })) + if(this.userLogins[username]){ + await Promise.all (Object.values(this.userLogins[username].connections).map(async (sock) => { + await sock.call('navigate', '/auth/login') + })) + } Object.values(this.rankServers) .forEach(state => { @@ -263,10 +276,11 @@ implements FrontworkComponent, ILoginMa } login = async(username:string, pwHash:string) : Promise => { + username = username.toLowerCase() const res:User[] = await this.admin.knex .select('*') .from('users') - .where({ name: username }) + .where({ username: username }) if(res.length > 0 && pwHash === res[0].pwhash){ const user:User = res[0] @@ -284,9 +298,8 @@ implements FrontworkComponent, ILoginMa port: this.rankServers[user.rank].port } - this.userLogins[user.name] = {connections: {}, auth: userAuth, user:user} + this.userLogins[user.username] = {connections: {}, auth: userAuth, user:user} this.rankServers[user.rank].allowed.push(token.value) - return userAuth } @@ -294,16 +307,14 @@ implements FrontworkComponent, ILoginMa } getUserRecordByToken(tokenValue: string){ - const maybeRecord = Object.values(this.userLogins).find(login => login.auth.token.value === tokenValue) - return maybeRecord?maybeRecord:undefined + return Object.values(this.userLogins).find(login => login.auth.token.value === tokenValue) } - getAuth = async (tokenValue:string) : Promise => { + getAuth = async (tokenValue:string) : Promise => { const maybeAuth = this.getUserRecordByToken(tokenValue) if(maybeAuth) return maybeAuth.auth - - throw new Error("Bad token") + return } startRankServer = async (rank : Rank, port: number) : Promise => { @@ -322,9 +333,14 @@ implements FrontworkComponent, ILoginMa }, connectionHandler: (socket) => { - this.checkConnection(socket).catch((e) => { - console.log(e); - }) //sometimes times out if you go too fast + this.checkConnection(socket).then(res => { + if(!res){ + socket.destroy(); + } + }).catch((e) => { + socket.destroy(); + getLogger('LoginManager').warn(e); + }) }, errorHandler: (socket, e, rpcName, args) => { console.log(rpcName, args); @@ -346,8 +362,8 @@ implements FrontworkComponent, ILoginMa createToken = (user:User): Token => { - if(this.userLogins[user.name]){ - return this.userLogins[user.name].auth.token + if(this.userLogins[user.username]){ + return this.userLogins[user.username].auth.token } const token:Token = { diff --git a/src/backend/Components/Raid/Interface.ts b/src/backend/Components/Raid/Interface.ts index eb0efde..0b77d21 100644 --- a/src/backend/Components/Raid/Interface.ts +++ b/src/backend/Components/Raid/Interface.ts @@ -1,4 +1,4 @@ -import { User, Raid, Signup } from "../../Types/Types" +import { Raid, Signup, Character } from "../../Types/Types" export class IRaidManager{ getRaids: () => Promise @@ -6,5 +6,5 @@ export class IRaidManager{ addSignup: (signup: Signup) => Promise removeSignup: (signup: Signup) => Promise getSignups: (raid:Raid) => Promise - sign: (user:User, raid:Raid, attending:boolean) => Promise + sign: (userToken: string, character:Character, raid:Raid, attending:boolean) => Promise } diff --git a/src/backend/Components/Raid/RaidManager.ts b/src/backend/Components/Raid/RaidManager.ts index 95b8390..a2dc6c8 100644 --- a/src/backend/Components/Raid/RaidManager.ts +++ b/src/backend/Components/Raid/RaidManager.ts @@ -1,9 +1,10 @@ import { Inject, Module } from "../../Injector/ServiceDecorator"; import { RaidManagerIfc, RaidManagerFeatureIfc } from "./RPCInterface"; import { FrontworkComponent } from "../../Types/FrontworkComponent"; -import { TableDefiniton, Signup, Raid, User } from "../../Types/Types"; +import { TableDefiniton, Signup, Raid, User, Character } from "../../Types/Types"; import { IAdmin } from "../../Admin/Interface"; import { IRaidManager } from "./Interface"; +import { ILoginManager } from "../Login/Interface"; @Module(IRaidManager) export class RaidManager @@ -13,6 +14,9 @@ implements FrontworkComponent, IRaidManag @Inject(IAdmin) private admin: IAdmin + @Inject(ILoginManager) + private login: ILoginManager + exportRPCs = () => [{ name: 'getRaids' as 'getRaids', call: this.getRaids @@ -57,11 +61,11 @@ implements FrontworkComponent, IRaidManag },{ name: 'signups', tableBuilder: (table) => { - table.primary(['raid_id', 'user_id']) - table.integer('raid_id') - table.foreign('raid_id').references('id').inTable('raids') - table.integer('user_id') - table.foreign('user_id').references('id').inTable('users') + table.primary(['raidid', 'characterid']) + table.integer('raidid') + table.foreign('raidid').references('id').inTable('raids') + table.integer('characterid') + table.foreign('characterid').references('id').inTable('characters') } } ] @@ -78,8 +82,8 @@ implements FrontworkComponent, IRaidManag removeSignup = async (signup: Signup) => await this.admin .knex('signups') .where({ - raid_id: signup.raid_id, - user_id: signup.user_id + raid_id: signup.raidid, + character_id: signup.characterid }) .delete() @@ -87,15 +91,25 @@ implements FrontworkComponent, IRaidManag .select('*') .from('raids') - getSignups = async (raid:Raid) : Promise => await this.admin.knex - .select('*') - .from('signups') - .where('raid_id', '=', raid.id!) - - sign = async (user:User, raid:Raid) => await this.admin + getSignups = async (raid:Raid) : Promise => await this.admin .knex('signups') - .insert({ - raid_id: raid.id!, - user_id: user.id! - }) + .join('characters as c', 'c.id', '=', 'characterid') + .join('specs as s', 's.id', '=', 'specid') + .join('users as u', 'u.id', '=', 'userid') + .select('*') + .where('raidid', '=', raid.id!) + + sign = async (usertoken:string, character:Character, raid:Raid) => { + const maybeUserRecord = this.login.getUserRecordByToken(usertoken) + if(!maybeUserRecord || maybeUserRecord.user.id != character.userid){ + throw new Error("Bad Usertoken") + } + + await this.admin + .knex('signups') + .insert({ + raidid: raid.id!, + characterid: character.id! + }) + } } \ No newline at end of file diff --git a/src/backend/Injector/Injector.ts b/src/backend/Injector/Injector.ts index 76e7d8a..154ce60 100644 --- a/src/backend/Injector/Injector.ts +++ b/src/backend/Injector/Injector.ts @@ -13,7 +13,7 @@ export const Injector = new class { rootInterface : Type root : Type rootModules: Type[] = [] - modules : {ifc?: Type, implementation: Type}[] = [] + modules : {implements?: Type, implementation: Type}[] = [] moduleObjs : {[key in string] : FrontworkComponent} = {} @@ -31,8 +31,8 @@ export const Injector = new class { if(target.name === this.rootInterface.name || target.name === this.root.name){ let modules = this.modules.map(m => { const module = new m.implementation() - if(m.ifc) - this.moduleObjs[m.ifc.name] = module + if(m.implements) + this.moduleObjs[m.implements.name] = module this.moduleObjs[m.implementation.name] = module return module }) diff --git a/src/backend/Injector/ServiceDecorator.ts b/src/backend/Injector/ServiceDecorator.ts index c202836..046da08 100644 --- a/src/backend/Injector/ServiceDecorator.ts +++ b/src/backend/Injector/ServiceDecorator.ts @@ -9,7 +9,7 @@ import { FrontworkComponent } from "../Types/FrontworkComponent"; export const Module = (ifc?: Type) : GenericClassDecorator> => { return (target: Type) => { Injector.modules.push({ - ifc: ifc, + extends: ifc, implementation: target }) } @@ -20,12 +20,12 @@ export const Module = (ifc?: Type) : GenericClassDecorator> => { * @constructor */ export const RootComponent = (config : { - rootInterface : Type + implements : Type imports : Type[] }) : GenericClassDecorator> => { return (target: Type) => { Injector.rootModules = config.imports - Injector.rootInterface = config.rootInterface + Injector.rootInterface = config.implements Injector.root = target } } diff --git a/src/backend/Types/PlayerSpecs.ts b/src/backend/Types/PlayerSpecs.ts index d03529e..e097217 100644 --- a/src/backend/Types/PlayerSpecs.ts +++ b/src/backend/Types/PlayerSpecs.ts @@ -1,4 +1,4 @@ -import { Spec, _Class } from "./Types" +import { Spec, _Class, Class } from "./Types" export type SpecT = { Warrior : 'Arms' | 'Fury' | 'Protection' @@ -74,9 +74,23 @@ export function getSpecTableData() : Spec[]{ const specNames : string[] = specs[_class] return specNames.map(specName => { return { - name: specName, + specname: specName, class: _class } }) }) +} + +export function getClassColor(c: Class) : string{ + switch(c){ + case "Warrior": return "#C79C6E" + case "Warlock": return "#8787ED" + case "Shaman": return "#0070DE" + case "Rogue": return "#FFF569" + case "Priest": return "#FFFFFF" + case "Paladin": return "#F58CBA" + case "Mage": return "#40C7EB" + case "Druid": return "#FF7D0A" + case "Hunter": return "#A9D271" + } } \ No newline at end of file diff --git a/src/backend/Types/Types.ts b/src/backend/Types/Types.ts index aa88ce8..0fe411b 100644 --- a/src/backend/Types/Types.ts +++ b/src/backend/Types/Types.ts @@ -1,5 +1,5 @@ import * as Knex from "knex" -import { RPCExporter } from "rpclibrary"; +import { RPCExporter, Socket } from "rpclibrary"; import { RaidManagerIfc, RaidManagerFeatureIfc } from "../Components/Raid/RPCInterface"; import { LoginManagerIfc, LoginManagerFeatureIfc } from "../Components/Login/RPCInterface"; import { CharacterManagerIfc, CharacterManagerFeatureIfc } from "../Components/Character/RPCInterface"; @@ -33,14 +33,14 @@ export const _Class : Class[] = ["Warrior" , "Rogue" , "Hunter" , "Mage" , "Warl export type AnyRPCExporter = RPCExporter export type RPCPermission = { - name: string + rpcnamename: string } & { [rank in Rank] : boolean } export type Item = { id?:number - name:string + itemname:string iconname:string url:string quality:string @@ -49,7 +49,7 @@ export type Item = { export type User = { id?: number - name: string + username: string pwhash: string rank: Rank email?: string @@ -65,13 +65,13 @@ export type Raid = { } export type Signup = { - raid_id: number - user_id: number + raidid: number + characterid: number } export type Character = { id? : number - name : string + charactername : string specid : number userid : number } @@ -84,11 +84,16 @@ export type Token = { export type Auth = {port: number, user: User, token: Token} +export type UserRecord = { + user: User + connections: {[port in number]: Socket} + auth: Auth +} export type Spec = { id?: number, class: Class, - name: string + specname: string } export type SomeOf = { diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 5132121..d20b3bf 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -17366,9 +17366,9 @@ } }, "tslib": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", - "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==" + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" }, "tslint": { "version": "5.7.0", diff --git a/src/frontend/package.json b/src/frontend/package.json index b25bea9..12ef5dc 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -74,7 +74,7 @@ "rxjs-compat": "6.3.0", "socicon": "3.0.5", "tinymce": "4.5.7", - "tslib": "^1.9.0", + "tslib": "^1.10.0", "typeface-exo": "0.0.22", "web-animations-js": "github:angular/web-animations-js#release_pr208", "zone.js": "~0.9.1" 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 c3d93df..d902992 100644 --- a/src/frontend/src/app/@theme/components/header/header.component.html +++ b/src/frontend/src/app/@theme/components/header/header.component.html @@ -16,7 +16,7 @@ diff --git a/src/frontend/src/app/@theme/components/header/header.component.ts b/src/frontend/src/app/@theme/components/header/header.component.ts index 354d71b..1846d92 100644 --- a/src/frontend/src/app/@theme/components/header/header.component.ts +++ b/src/frontend/src/app/@theme/components/header/header.component.ts @@ -39,7 +39,7 @@ export class HeaderComponent implements OnInit, OnDestroy { currentTheme = 'dark'; - userMenu : NbMenuItem[] = [ { title: 'Profile' }, { title: 'Log out', url: '/auth/logout' } ]; + userMenu : NbMenuItem[] = [ { title: 'Log out', link: '/auth/logout' } ]; constructor(private sidebarService: NbSidebarService, private menuService: NbMenuService, @@ -53,6 +53,8 @@ export class HeaderComponent implements OnInit, OnDestroy { this.currentTheme = this.themeService.currentTheme; this.user = this.loginService.getCurrentUser() + if(this.user) + this.userMenu.unshift({ title: 'Profile', link: '/frontcraft/user/'+this.user.username }); /* this.userService.getUsers() diff --git a/src/frontend/src/app/app-routing.module.ts b/src/frontend/src/app/app-routing.module.ts index 08349a1..bdcb25f 100644 --- a/src/frontend/src/app/app-routing.module.ts +++ b/src/frontend/src/app/app-routing.module.ts @@ -2,6 +2,7 @@ import { ExtraOptions, RouterModule, Routes } from '@angular/router'; import { NgModule } from '@angular/core'; const routes: Routes = [ + { path: 'auth', loadChildren: () => import('./frontcraft/auth/auth.module') diff --git a/src/frontend/src/app/app.module.ts b/src/frontend/src/app/app.module.ts index 24633f7..9ec1715 100644 --- a/src/frontend/src/app/app.module.ts +++ b/src/frontend/src/app/app.module.ts @@ -20,7 +20,6 @@ import { NbToastrModule, NbWindowModule, } from '@nebular/theme'; -import { LoginApiService } from './frontcraft/services/login-api'; @NgModule({ declarations: [AppComponent], diff --git a/src/frontend/src/app/frontcraft/auth/register/register.component.html b/src/frontend/src/app/frontcraft/auth/register/register.component.html index 40d12e5..dfc5326 100644 --- a/src/frontend/src/app/frontcraft/auth/register/register.component.html +++ b/src/frontend/src/app/frontcraft/auth/register/register.component.html @@ -21,7 +21,7 @@ { + 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! + }) + }) + }catch(e){ + alert("Error creating character"+e) + return } } diff --git a/src/frontend/src/app/frontcraft/pages/character/character.component.html b/src/frontend/src/app/frontcraft/pages/character/character.component.html new file mode 100644 index 0000000..5951352 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/character/character.component.html @@ -0,0 +1,10 @@ + + + {{char.charactername}} + + + {{char.specname}} {{char.class}}
+ Owned by {{char.username}} ({{char.rank}}) +
+
diff --git a/src/frontend/src/app/frontcraft/pages/character/character.component.ts b/src/frontend/src/app/frontcraft/pages/character/character.component.ts new file mode 100644 index 0000000..e99d709 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/character/character.component.ts @@ -0,0 +1,34 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { LoginApiService } from '../../services/login-api'; +import { Spec, User, Character } from '../../../../../../backend/Types/Types'; +import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; + +@Component({ + selector: 'character', + templateUrl: './character.component.html', +}) +export class FrontcraftCharacterComponent implements OnInit{ + + char : (Character & User & Spec) = {} as any + color : string + + constructor( + private login: LoginApiService, + private route: ActivatedRoute, + private router: Router, + ){} + + async ngOnInit(){ + const param = this.route.snapshot.paramMap.get('name'); + this.login.getUnprivilegedSocket() + .CharacterManager + .getCharacterByName(param) + .then((char) => { + if(char){ + this.color = getClassColor(char.class) + this.char = char + } + }) + } +} \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.html b/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.html index 28aec0d..4362e2a 100644 --- a/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.html +++ b/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.html @@ -1 +1,35 @@ - Dashboard \ No newline at end of file + + + + + + + + + + + + + + + + + + + + +
+ {{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 new file mode 100644 index 0000000..5667cf7 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.scss @@ -0,0 +1,42 @@ +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 index 71790cd..365631c 100644 --- a/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.ts +++ b/src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.ts @@ -1,16 +1,63 @@ -import { Component, OnInit } from '@angular/core'; -import { LoginApiService } from '../../services/login-api'; +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 implements OnInit{ +export class FrontcraftDashboardComponent{ + customColumn = 'name'; + defaultColumns = [ 'character', 'SRC'/*, 'Profile'*/ ]; + allColumns = [ this.customColumn, ...this.defaultColumns ]; - constructor(private loginApi : LoginApiService){} + dataSource: NbTreeGridDataSource; - ngOnInit(){ - console.log(this.loginApi) + 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; + } -} \ No newline at end of file + 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 new file mode 100644 index 0000000..e490e9a --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/dashboard/tree-grid-shared.scss @@ -0,0 +1,10 @@ +::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 48a7e2a..d110615 100644 --- a/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts +++ b/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts @@ -17,10 +17,19 @@ import { Router } from '@angular/router'; export class PagesLayoutComponent implements OnInit{ menu:NbMenuItem[] = [{ icon: 'people-outline', - title: 'People' + title: 'People', + link: '/frontcraft/people' },{ icon: 'clock-outline', title: 'Raids' + },{ + icon: 'clock-outline', + title: 'character', + link: '/frontcraft/character/a' + },{ + icon: 'clock-outline', + title: 'user', + link: '/frontcraft/user/a' }] constructor( 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 aa771f4..336d30e 100644 --- a/src/frontend/src/app/frontcraft/pages/pages-routing.module.ts +++ b/src/frontend/src/app/frontcraft/pages/pages-routing.module.ts @@ -2,12 +2,27 @@ 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'; export const routes: Routes = [ { path: '', component: PagesLayoutComponent, children: [ + { + path: 'people', + component: FrontcraftPeopleComponent + }, + { + path: 'user/:name', + component: FrontcraftUserComponent + }, + { + path: 'character/:name', + component: FrontcraftCharacterComponent + }, { path: 'dashboard', component: FrontcraftDashboardComponent, diff --git a/src/frontend/src/app/frontcraft/pages/pages.module.ts b/src/frontend/src/app/frontcraft/pages/pages.module.ts index c61e728..cbcb8ad 100644 --- a/src/frontend/src/app/frontcraft/pages/pages.module.ts +++ b/src/frontend/src/app/frontcraft/pages/pages.module.ts @@ -9,7 +9,8 @@ import { NbCheckboxModule, NbInputModule, NbMenuModule, - NbCardModule + NbCardModule, + NbTreeGridModule } from '@nebular/theme'; import { MyAuthRoutingModule } from './pages-routing.module'; import { FrontcraftDashboardComponent } from './dashboard/dashboard.component'; @@ -18,11 +19,15 @@ import { ThemeModule } from '../../@theme/theme.module'; import { DashboardModule } from '../../demo_pages/dashboard/dashboard.module'; 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'; @NgModule({ imports: [ MyAuthRoutingModule, + NbTreeGridModule, CommonModule, FormsModule, RouterModule, @@ -39,6 +44,9 @@ import { MiscellaneousModule } from '../../demo_pages/miscellaneous/miscellaneou ], declarations: [ + FrontcraftPeopleComponent, + FrontcraftUserComponent, + FrontcraftCharacterComponent, PagesLayoutComponent, FrontcraftDashboardComponent ], diff --git a/src/frontend/src/app/frontcraft/pages/people/people.component.html b/src/frontend/src/app/frontcraft/pages/people/people.component.html new file mode 100644 index 0000000..f37cf9b --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/people/people.component.html @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + +
+ {{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 new file mode 100644 index 0000000..5667cf7 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/people/people.component.scss @@ -0,0 +1,42 @@ +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 new file mode 100644 index 0000000..93558d7 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/people/people.component.ts @@ -0,0 +1,64 @@ +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 new file mode 100644 index 0000000..e490e9a --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/people/tree-grid-shared.scss @@ -0,0 +1,10 @@ +::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/user/user.component.html b/src/frontend/src/app/frontcraft/pages/user/user.component.html new file mode 100644 index 0000000..b4aa189 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/user/user.component.html @@ -0,0 +1,24 @@ + + + + +

{{user.username}}

+
+ + + +

+ + {{char.charactername}} + +

+
+ + {{char.specname}} {{char.class}} + +
+
+
diff --git a/src/frontend/src/app/frontcraft/pages/user/user.component.ts b/src/frontend/src/app/frontcraft/pages/user/user.component.ts new file mode 100644 index 0000000..08915b6 --- /dev/null +++ b/src/frontend/src/app/frontcraft/pages/user/user.component.ts @@ -0,0 +1,38 @@ +import { Component, OnInit } from '@angular/core'; +import { LoginApiService } from '../../services/login-api'; +import { Router } from '@angular/router'; +import { User, Spec, Character } from '../../../../../../backend/Types/Types'; +import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; + +@Component({ + selector: 'user-component', + templateUrl: './user.component.html', +}) +export class FrontcraftUserComponent implements OnInit{ + + user:User = {} as any + characters : (Character & Spec)[] = [] + + constructor( + private router : Router, + private loginApi : LoginApiService + ){} + + 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 + }) + } +} \ 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 fb3aa52..a912218 100644 --- a/src/frontend/src/app/frontcraft/services/login-api.ts +++ b/src/frontend/src/app/frontcraft/services/login-api.ts @@ -31,18 +31,20 @@ export class LoginApiService{ const authSock = await sock.connect>(auth.token.value) - sock.hook('kick', () => this.logout()) + sock.hook('kick', () => { + console.log("I got kicked"); + }) sock.hook('getUserData', () => auth) sock.hook('navigate', (where:string) => { - this.ngZone.run(() => { - this.injector.get(Router).navigateByUrl(where); + this.ngZone.run( () => { + this.injector.get(Router).navigateByUrl('/') }) }) sock.on('error', (e) => { - console.log('Socket error', e) - this.logout() + sock.destroy(); + }) - + this.auth = auth this.privSocket = authSock this.cookieSvc.set('token', JSON.stringify(auth)) @@ -62,7 +64,9 @@ export class LoginApiService{ } } - getCurrentUser = () : User | undefined => this.auth?this.auth.user:undefined + getCurrentUser = () : User | undefined => { + return this.auth?this.auth.user:undefined + } login = async (username: string, password: string) : Promise> => { const pwHash = await hash(password) @@ -78,7 +82,7 @@ export class LoginApiService{ logout = async () => { this.cookieSvc.set('token', undefined) - if(this.auth) await this.socket.Authenticator.logout(this.auth.user.name, this.auth.token.value) + if(this.auth) await this.socket.Authenticator.logout(this.auth.user.username, this.auth.token.value) if(this.privSocket) this.privSocket.destroy() this.privSocket = null this.auth = null