Added dependency injection

This commit is contained in:
peter
2020-01-22 00:58:12 +01:00
parent 030908376b
commit e4a6972884
27 changed files with 1769 additions and 208 deletions
+1259 -40
View File
File diff suppressed because it is too large Load Diff
+8 -2
View File
@@ -4,8 +4,9 @@
"version": "1.0.0",
"scripts": {
"tsc": "tsc",
"start": "npm run build; node lib/Launcher.js",
"start": "npm run build; node lib/src/Launcher.js",
"build": "npm run clean; npm run build-backend; npm run build-frontend",
"test": "npm run clean && npm run build-backend && mocha lib/test/backendTest.js",
"build-backend": "tsc;",
"build-frontend": "mkdir dist; mkdir dist/static; npm run build-dashboard;",
"build-dashboard": "cd src/frontend; npm i && npm run build; cp -r dist/* ../../dist/static",
@@ -20,6 +21,7 @@
"author": "frontblock.me",
"license": "ISC",
"dependencies": {
"@types/mocha": "^5.2.7",
"bsert": "0.0.10",
"bsock": "^0.1.9",
"child-process-promise": "^2.2.1",
@@ -34,14 +36,17 @@
"loadson": "^1.0.0",
"log4js": "^4.5.1",
"lowdb": "^1.0.0",
"mocha": "^7.0.0",
"node-fetch": "^2.6.0",
"path": "^0.12.7",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.0",
"rpclibrary": "^1.5.2",
"rpclibrary": "^1.6.2",
"simple-git": "^1.124.0",
"spawn-sync": "^2.0.0",
"sqlite3": "^4.1.0",
"trash": "^6.0.0",
"tsyringe": "^4.0.1",
"upgiter": "^1.0.4",
"uuid": "^3.3.3",
"xml2js": "^0.4.22"
@@ -50,6 +55,7 @@
"@types/express": "^4.17.0",
"@types/node": "^11.13.19",
"@types/semver": "^6.0.1",
"madge": "^3.6.0",
"terser-webpack-plugin": "^1.4.1",
"ts-loader": "^5.3.3",
"typescript": "^3.5.3",
+30 -14
View File
@@ -3,19 +3,37 @@
import { getLogger } from 'frontblock-generic/Types';
import { promises as fs, mkdirSync } from "fs"
import { RPCServer } from 'rpclibrary'
import { AdminConf, TableDefiniton } from '../Types/Types';
import { RPCConfigLoader } from '../Components/RPCConfigLoader';
import * as Path from 'path'
import * as Knex from 'knex';
import * as http from 'http';
import * as express from 'express';
import { GuildManager } from '../Components/Guild/GuildManager';
import { ItemManager } from '../Components/Item/ItemManager';
import { RaidManager } from '../Components/Raid/RaidManager';
import { CharacterManager } from '../Components/User/CharacterManager';
import { LoginManager } from '../Components/User/LoginManager';
import { RootComponent } from '../Injector/ServiceDecorator';
import { TableDefinitionExporter } from '../Types/Interfaces';
import { AdminConf, TableDefiniton } from '../Types/Types';
import { RPCConfigLoader } from '../Components/RPCConfigLoader';
import { FrontworkComponent } from '../Types/FrontworkComponent';
import { IAdmin } from './Interface';
const logger = getLogger("admin", 'debug')
@RootComponent({
rootInterface: IAdmin,
imports: [
GuildManager,
ItemManager,
RaidManager,
CharacterManager,
LoginManager
]
})
export class FrontworkAdmin
implements TableDefinitionExporter {
implements TableDefinitionExporter, IAdmin {
knex:Knex
config: RPCConfigLoader<AdminConf>
rpcServer: RPCServer
@@ -23,7 +41,7 @@ implements TableDefinitionExporter {
private express
private httpServer
constructor(private components: FrontworkComponent[]){
constructor(private frontworkComponents: FrontworkComponent[] = []){
this.config = new RPCConfigLoader<AdminConf>({
name: "FrontworkAdminConf",
getDefaultConfig: () => {
@@ -40,19 +58,17 @@ implements TableDefinitionExporter {
}
}
}, './config', this.configChangeHandler)
components.forEach(c => {
c.admin = this
if(c.onSetAdmin) c.onSetAdmin(this)
})
}
async start(){
await this.makeKnex()
this.startWebsocket()
await Promise.all( this.components.map(c => c.initialize?c.initialize():undefined ))
await Promise.all( this.frontworkComponents.map(c => c.initialize?c.initialize():undefined ))
this.startWebserver()
}
stop(){
process.exit(0);
}
protected configChangeHandler = (conf:AdminConf, key?:string) => {
@@ -71,13 +87,13 @@ implements TableDefinitionExporter {
getTableDefinitions(): TableDefiniton[]{
return [
...this.components
...this.frontworkComponents
].flatMap(exporter => exporter.getTableDefinitions())
}
private startWebsocket(){
this.rpcServer = new RPCServer(20000, [
...this.components,
...this.frontworkComponents,
])
}
@@ -169,4 +185,4 @@ process.on( 'SIGINT', function() {
logger.info("Shutting down from SIGINT (Ctrl-C)" );
// some other closing procedures go here
process.exit(0);
})
})
+13
View File
@@ -0,0 +1,13 @@
import { RPCConfigLoader } from "../Components/RPCConfigLoader"
import { AdminConf } from "../Types/Types"
import { RPCServer } from "rpclibrary"
import Knex = require("knex")
export class IAdmin{
knex: Knex
config: RPCConfigLoader<AdminConf>
rpcServer: RPCServer
start: ()=>Promise<void>
stop: ()=>void
}
+9 -4
View File
@@ -1,8 +1,10 @@
import { ConfigLoader } from "loadson";
import { Inject, Module } from "../../Injector/ServiceDecorator";
import { GuildManagerFeatureIfc, GuildManagerIfc } from "./RPCInterface";
import { FrontworkAdmin } from "../../Admin/Admin";
import { FrontworkComponent } from "../../Types/FrontworkComponent";
import { _Rank, Rank } from "../../Types/Types";
import { ConfigLoader } from "loadson";
import { GuildManagerIfc, GuildManagerFeatureIfc } from "./RPCInterface";
import { IAdmin } from "../../Admin/Interface";
export type Guild = {
name: string
@@ -10,11 +12,14 @@ export type Guild = {
description: string
}
@Module()
export class GuildManager
implements FrontworkComponent<GuildManagerIfc, GuildManagerFeatureIfc>{
name = "GuildManager" as "GuildManager";
admin: FrontworkAdmin
@Inject(IAdmin)
private admin
guild: ConfigLoader<Guild>
@@ -56,7 +61,7 @@ implements FrontworkComponent<GuildManagerIfc, GuildManagerFeatureIfc>{
getTableDefinitions = () => []
headCount = async () => await Promise.all(
['ADMIN', ..._Rank].map(async r => {
_Rank.map(async r => {
const res = await this.admin.knex
.select('*')
.from('users')
+18 -10
View File
@@ -1,16 +1,17 @@
import { TableDefinitionExporter } from "../../Types/Interfaces";
import { TableDefiniton, _Rank } from "../../Types/Types";
import { FrontworkAdmin } from "../../Admin/Admin";
import { T1 } from "../../Types/Items";
import { RPC } from "rpclibrary";
import { Inject, Module } from "../../Injector/ServiceDecorator";
import { ItemManagerFeatureIfc, ItemManagerIfc } from "./RPCInterface";
import { FrontworkComponent } from "../../Types/FrontworkComponent";
import { RPC, RPCInterface } from "rpclibrary";
import { ItemManagerIfc, ItemManagerFeatureIfc } from "./RPCInterface";
import { TableDefinitionExporter } from "../../Types/Interfaces";
import { FrontworkAdmin } from "../../Admin/Admin";
import { TableDefiniton } from "../../Types/Types";
import { IAdmin } from "../../Admin/Interface";
const fetch = require('node-fetch')
const xml2js = require('xml2js');
const parser = new xml2js.Parser(/* options */);
export type Item = {
id?:number
name:string
@@ -20,12 +21,14 @@ export type Item = {
hidden:boolean
}
@Module()
export class ItemManager
implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefinitionExporter{
admin:FrontworkAdmin
name = "ItemManager" as "ItemManager";
@Inject(IAdmin)
private admin: FrontworkAdmin
exportRPCs(): RPC<any, any>[]{
return [{
name: 'getItems',
@@ -90,7 +93,11 @@ implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefin
return <number>count[0]['count(*)']
}
initialize = async() => {
private initialized = false
initialize = async () => {
if(!this.initialized)
this.initialized = true
const allItems = [...T1]
const countCache = await this.countItems()
if(countCache != allItems.length){
@@ -100,6 +107,7 @@ implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefin
.knex('items')
.insert(items)
}catch(e){
console.log(e)
console.info("Skipping item insertion")
}
}
+6 -3
View File
@@ -1,7 +1,10 @@
import { Raid, Signup, User } from "../../Types/Types";
import { RPCInterface } from "rpclibrary";
import { Raid, Signup, User } from "../../Types/Types"
export type RaidManagerIfc = RPCInterface
export type RaidManagerIfc = {
RaidManager:{
getRaids: () => Promise<Raid[]>
}
}
export type RaidManagerFeatureIfc = {
manageRaid: {
+17 -11
View File
@@ -1,14 +1,22 @@
import { TableDefiniton, User, _Rank, Raid, Signup } from "../../Types/Types";
import { FrontworkAdmin } from "../../Admin/Admin";
import { Inject, Module } from "../../Injector/ServiceDecorator";
import { RaidManagerIfc, RaidManagerFeatureIfc } from "./RPCInterface";
import { FrontworkComponent } from "../../Types/FrontworkComponent";
import { RaidManagerFeatureIfc, RaidManagerIfc } from "./RPCInterface";
import { FrontworkAdmin } from "../../Admin/Admin";
import { TableDefiniton, Signup, Raid, User } from "../../Types/Types";
import { IAdmin } from "../../Admin/Interface";
@Module()
export class RaidManager
implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>{
name = "RaidManager" as "RaidManager";
admin: FrontworkAdmin
exportRPCs = () => []
@Inject(IAdmin)
private admin: FrontworkAdmin
exportRPCs = () => [{
name: 'getRaids' as 'getRaids',
call: this.getRaids
},]
exportRPCFeatures() {
return [{
@@ -26,9 +34,6 @@ implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>{
},{
name: 'signup' as 'signup',
exportRPCs: () => [{
name: 'getRaids' as 'getRaids',
call: this.getRaids
},{
name: 'getSingups' as 'getSingups',
call: this.getSignups
},{
@@ -52,9 +57,10 @@ implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>{
},{
name: 'signups',
tableBuilder: (table) => {
table.integer('raid_id').primary()
table.primary(['raid_id', 'user_id'])
table.integer('raid_id')
table.foreign('raid_id').references('id').inTable('raids')
table.integer('user_id').primary()
table.integer('user_id')
table.foreign('user_id').references('id').inTable('users')
}
}
@@ -77,7 +83,7 @@ implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>{
})
.delete()
getRaids = async () => await this.admin.knex
getRaids = async () : Promise<Raid[]> => await this.admin.knex
.select('*')
.from('raids')
@@ -0,0 +1,96 @@
import { RPCInterface } from "rpclibrary";
import { Inject, Module } from "../../Injector/ServiceDecorator";
import { TableDefiniton, Character } from "../../Types/Types";
import { CharacterManagerIfc } from "./RPCInterface";
import { FrontworkComponent } from "../../Types/FrontworkComponent";
import { FrontworkAdmin } from "../../Admin/Admin";
import { LoginManager } from "./LoginManager";
import { getSpecTableData, SpecT } from "../../Types/PlayerSpecs";
import { IAdmin } from "../../Admin/Interface";
@Module()
export class CharacterManager
implements FrontworkComponent<CharacterManagerIfc, RPCInterface>{
name = "CharacterManager" as "CharacterManager";
@Inject(IAdmin)
private admin: IAdmin
private loginManager : LoginManager
exportRPCs = () => [
{
name: 'getSpecId' as 'getSpecId',
call: this.getSpecId
}
]
exportRPCFeatures = () => [
{
name: "createCharacter",
exportRPCs: () => [
{
name: 'createCharacter',
call: this.createCharacter
}
]
}
]
getTableDefinitions = (): TableDefiniton[] => [
{
name: 'characters',
tableBuilder: (table) => {
table.increments("id").primary()
table.string("name").notNullable().unique()
table.integer("specid").notNullable()
table.foreign("specid").references("specs.id")
table.integer("userid").notNullable()
table.foreign("userid").references("users.id")
}
},{
name: 'specs',
tableBuilder: (table) => {
table.increments("id")
table.string('class')
table.string('name')
table.unique(['class', 'name'])
}
}
]
private initialized = false
initialize = async () => {
if(!this.initialized)
this.initialized = true
//initialize spec table
await this.admin.knex('specs').insert(getSpecTableData()).catch(e => { console.log("skipping spec insertion") })
}
createCharacter = async (userToken: string, character : Character) : Promise<Character> => {
try{
await this.admin.knex('characters').insert(character)
const char : Character = await this.admin.knex.select('*').from('characters').where(character).first()
return char
}catch(e){
console.log(e);
}
throw new Error('Unable to create character')
}
getCharacters = async() : Promise<Character[]> => {
return this.admin.knex.select('*').from('characters')
}
getSpecId = async <c extends keyof SpecT>(clazz: c, name: SpecT[c]) => await this.admin.knex
.from('specs')
.select('id')
.where({
class: clazz,
name: name
}).first().then(spec => spec.id)
}
+78 -51
View File
@@ -1,10 +1,15 @@
import { RPCServer, Socket } from "rpclibrary";
import { TableDefiniton, AnyRPCExporter, User, RPCPermission, Token, Auth, Rank, FrontcraftFeatureIfc, _Rank } from "../../Types/Types";
import { Inject, Module } from "../../Injector/ServiceDecorator";
import { FrontworkAdmin } from "../../Admin/Admin";
import { PrivilegedRPCExporter } from "../../Types/PrivilegedRPCExporter";
import { FrontworkComponent } from "../../Types/FrontworkComponent";
import { getSpecTableData } from "../../Types/PlayerSpecs"
import { GuildManager } from "../Guild/GuildManager";
import { ItemManager } from "../Item/ItemManager";
import { RaidManager } from "../Raid/RaidManager";
import { CharacterManager } from "./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 { IAdmin } from "../../Admin/Interface";
const uuid = require('uuid/v4')
const ONE_WEEK = 604800000
@@ -15,21 +20,35 @@ type Serverstate = {
allowed: string[]
}
@Module()
export class LoginManager
implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>{
name = "Authenticator" as "Authenticator";
admin:FrontworkAdmin
@Inject(IAdmin)
private admin: FrontworkAdmin
@Inject(GuildManager)
private guild : GuildManager
@Inject(ItemManager)
private item : ItemManager
@Inject(RaidManager)
private raid : RaidManager
@Inject(CharacterManager)
private character : CharacterManager
exporters :any[] = []
rankServers : {[rank in Rank] : Serverstate}
userLogins : {[username in string] : {
user: User
connections: {[port in number]: Socket}
auth: Auth
}} = {}
constructor(
private exporters: PrivilegedRPCExporter[]
){}
exportRPCs = () => [
{
name: 'login' as 'login',
@@ -43,21 +62,14 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>{
},{
name: 'checkToken' as 'checkToken',
call: async (tokenValue : string, rank: Rank) => this.checkToken(tokenValue, rank)
},{
name: 'createUser' as 'createUser',
call: this.createUser
}
]
onSetAdmin(admin:FrontworkAdmin){
this.exporters.forEach(e => e['admin'] = admin)
}
exportRPCFeatures = () => [
{
name: 'createUser' as 'createUser',
exportRPCs: () => [{
name: 'createUser' as 'createUser',
call: this.createUser
}]
},{
name: 'modifyPermissions' as 'modifyPermissions',
exportRPCs: () => [{
name: 'getPermissions' as 'getPermissions',
@@ -79,36 +91,26 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>{
table.string("pwhash").notNullable()
table.string("rank").notNullable()
table.string("email").nullable().unique()
}
},{
name: 'characters',
tableBuilder: (table) => {
table.increments("id").primary()
table.string("name").notNullable().unique()
table.integer("specid").notNullable()
table.foreign("specid").references("specs.id")
table.integer("userid").notNullable()
table.foreign("userid").references("users.id")
table.boolean("locked").defaultTo(true)
}
},{
name: 'rpcpermissions',
tableBuilder: (table) => {
table.string("name").primary().notNullable()
table.boolean("ADMIN").defaultTo(true).notNullable()
_Rank.forEach(r => table.boolean(r).defaultTo(false).notNullable())
}
},{
name: 'specs',
tableBuilder: (table) => {
table.increments("id")
table.string('class')
table.string('name')
_Rank.forEach(r => {
if(r === 'ADMIN')
table.boolean(r).defaultTo(true).notNullable()
else
table.boolean(r).defaultTo(false).notNullable()
})
}
}
,...this.exporters.flatMap(exp => exp['getTableDefinitions']?exp['getTableDefinitions']():undefined)
]
initialize = async () => {
this.exporters = [this.guild, this.item, this.raid, this.character]
//set up permissions
await Promise.all(
[this, ...this.exporters].flatMap(exp => exp.exportRPCFeatures().map(async (feature) => {
@@ -117,12 +119,6 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>{
}catch(e){}
})))
//initialize managed exporters
await Promise.all(this.exporters.map(ex => ex['initialize']?ex['initialize']():undefined))
//initialize spec table
await this.admin.knex('specs').insert(getSpecTableData()).catch(e => { console.log("skipping spec insertion") })
//start rankServers
const rankServers : any = {}
@@ -138,7 +134,7 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>{
}
this.rankServers = rankServers
setInterval(this.checkExpiredSessions, 600_000)
setInterval(this.checkExpiredSessions, 600_000)
}
checkExpiredSessions = () => {
@@ -151,8 +147,14 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>{
}
checkConnection = async (socket: Socket) => {
let data : Auth | false = false
let data : any = false
let tries = 0
while(!data){
tries ++
if(tries === 5){
socket.destroy()
return
}
data = await Promise.race([socket.call('getUserData'), new Promise((res, rej) => { setTimeout(res, 250);})])
}
this.userLogins[data.user.name].connections[socket.port] = socket
@@ -181,20 +183,40 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>{
}
getRPCForRank = async (rank: Rank): Promise<AnyRPCExporter[]> => {
return [
let rpcs = [
...this.exportRPCFeatures(),
...this.exporters.flatMap((exp) => exp.exportRPCFeatures())
].filter(async (feature) => await this.getPermission(<keyof FrontcraftFeatureIfc> feature.name, rank))
]
const bits = await Promise.all(rpcs.map(async (feature) => {
const allowed = await this.getPermission(<keyof FrontcraftFeatureIfc> feature.name, rank)
return allowed
}))
return rpcs.filter(entry => bits.shift())
}
createUser = async(user:User): Promise<User> => {
if(user.rank === 'ADMIN'){
const admins = await this.admin.knex
.select("*")
.from('users')
.where({rank: 'ADMIN'})
if(admins.length > 0){
return {} as User
}
user.locked = false
}
await this.admin.knex('users')
.insert(user)
const users = await this.admin.knex
.select("*")
.from('users')
.where(user)
return users[0]
}
@@ -238,7 +260,7 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>{
port: this.rankServers[user.rank].port
}
this.userLogins[user.name] = {connections: {}, auth: userAuth}
this.userLogins[user.name] = {connections: {}, auth: userAuth, user:user}
this.rankServers[user.rank].allowed.push(token.value)
return userAuth
@@ -247,8 +269,13 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>{
throw new Error('login failed')
}
getUserRecordByToken(tokenValue: string){
const maybeRecord = Object.values(this.userLogins).find(login => login.auth.token.value === tokenValue)
return maybeRecord?maybeRecord:undefined
}
getAuth = async (tokenValue:string) : Promise<Auth> => {
const maybeAuth = Object.values(this.userLogins).find(login => login.auth.token.value === tokenValue)
const maybeAuth = this.getUserRecordByToken(tokenValue)
if(maybeAuth)
return maybeAuth.auth
+16 -4
View File
@@ -1,4 +1,5 @@
import { Token, Auth, User, RPCPermission, Rank } from "../../Types/Types"
import { Auth, Rank, User, RPCPermission, Character } from "../../Types/Types"
import { SpecT } from "../../Types/PlayerSpecs"
export type LoginManagerIfc = {
Authenticator: {
@@ -6,15 +7,26 @@ export type LoginManagerIfc = {
logout: (username: string, tokenValue :string) => Promise<void>
getAuth: (tokenValue: string) => Promise<Auth>
checkToken: (token: string, rank: Rank) => Promise<boolean>
createUser: (user:User) => Promise<User>
}
}
export type LoginManagerFeatureIfc = {
createUser: {
createUser: (user:User) => Promise<User>
}
modifyPermissions: {
setPermission: (perm: RPCPermission) => Promise<void>
getPermissions: () => Promise<RPCPermission[]>
}
}
export type CharacterManagerIfc = {
CharacterManager: {
getSpecId : <c extends keyof SpecT>(clazz: c, name: SpecT[c]) => Promise<number>
getCharacters : () => Promise<Character[]>
}
}
export type CharacterManagerFeatureIfc = {
createCharacter: {
createCharacter: (usertoken: string, char : Character) => Promise<Character>
}
}
+50
View File
@@ -0,0 +1,50 @@
import 'reflect-metadata';
import { Type } from './Util';
import { FrontworkComponent } from '../Types/FrontworkComponent';
/**
* The Injector stores services and resolves requested instances.
*/
export const Injector = new class {
injectionQueue :any[] = []
rootInterface : Type<any>
root : Type<any>
rootModules : Type<any>[] = []
moduleObjs : {[key in string] : FrontworkComponent} = {}
/**
* Resolves instances by injecting required services
* @param {Type<any>} target
* @returns {T}
*/
resolve<T>(target: Type<any>): T {
// tokens are required dependencies, while injections are resolved tokens from the Injector
if(this.moduleObjs[target.name])
return this.moduleObjs[target.name] as any
if(target.name === this.rootInterface.name || target.name === this.root.name){
let modules = this.rootModules.map(m => {
const module = new m()
this.moduleObjs[m.name] = module
return module
})
const rootobj = new this.root(modules);
this.moduleObjs[this.rootInterface.name] = rootobj
this.moduleObjs[target.name] = rootobj
this.injectionQueue.forEach(i => {
i.target[i.where] = this.moduleObjs[i.what.name]
})
return rootobj
}
this.moduleObjs[target.name] = new target()
return this.moduleObjs[target.name] as any
}
};
+34
View File
@@ -0,0 +1,34 @@
import { Injector } from "./Injector";
import { Type, GenericClassDecorator } from "./Util";
import { FrontworkComponent } from "../Types/FrontworkComponent";
/**
* @returns {GenericClassDecorator<Type<any>>}
* @constructor
*/
export const Module = (...args) : GenericClassDecorator<Type<any>> => {
return (target: Type<any>) => {
Injector.rootModules.push(target)
}
}
/**
* @returns {GenericClassDecorator<Type<any>>}
* @constructor
*/
export const RootComponent = (config : {
rootInterface : Type<any>
imports : Type<FrontworkComponent>[]
}) : GenericClassDecorator<Type<any>> => {
return (target: Type<any>) => {
Injector.rootModules = config.imports
Injector.rootInterface = config.rootInterface
Injector.root = target
}
}
export const Inject = (type: any) => {
return function (_this, key) {
Injector.injectionQueue.push({what: type, target: _this, where:key})
}
}
+11
View File
@@ -0,0 +1,11 @@
/**
* Type for what object is instances of
*/
export interface Type<T> {
new(...args: any[]): T;
}
/**
* Generic `ClassDecorator` type
*/
export type GenericClassDecorator<T> = (target: T) => void;
+3 -21
View File
@@ -1,23 +1,5 @@
import { FrontworkAdmin } from './Admin/Admin'
import { RaidManager } from "./Components/Raid/RaidManager";
import { ItemManager } from "./Components/Item/ItemManager";
import { LoginManager } from "./Components/User/LoginManager";
import { Debugger } from './Components/Debugger/Debugger';
import { FrontworkComponent } from './Types/FrontworkComponent';
import { GuildManager } from './Components/Guild/GuildManager';
import { Injector } from './Injector/Injector';
import { IAdmin } from './Admin/Interface';
require('events').EventEmitter.defaultMaxListeners = 0;
let raidManager = new RaidManager()
let itemManager = new ItemManager()
let guildManager = new GuildManager()
let loginManager = new LoginManager([
raidManager,
itemManager,
guildManager
])
let components:FrontworkComponent[] = [ guildManager, raidManager, itemManager, loginManager ]
let dbg = new Debugger(components)
new FrontworkAdmin([dbg, ...components]).start()
Injector.resolve<IAdmin>(IAdmin).start()
+1 -4
View File
@@ -1,7 +1,6 @@
import { PrivilegedRPCExporter } from "./PrivilegedRPCExporter";
import { RPCInterface, RPCExporter, RPCInterfaceArray } from "rpclibrary";
import { PrivilegedRPCExporter } from "./PrivilegedRPCExporter";
import { TableDefinitionExporter } from "./Interfaces";
import { FrontworkAdmin } from "../Admin/Admin";
import { TableDefiniton } from "./Types";
export interface FrontworkComponent<
@@ -14,7 +13,6 @@ export interface FrontworkComponent<
PrivilegedRPCExporter<Ifc, FeatureIfc, Name, FeatureName, SubresT>,
TableDefinitionExporter
{
admin:FrontworkAdmin
name: Name;
exportRPCFeatures(): RPCExporter<FeatureIfc, FeatureName, SubresT>[]
@@ -22,5 +20,4 @@ export interface FrontworkComponent<
getTableDefinitions(): TableDefiniton[]
initialize?(): Promise<any>
onSetAdmin?(admin:FrontworkAdmin): void
}
+14 -2
View File
@@ -1,6 +1,18 @@
import { Class, _Class, Spec } from "./Types";
import { Spec, _Class } from "./Types"
const specs : { [classname in Class] : string[] } = {
export type SpecT = {
Warrior : 'Arms' | 'Fury' | 'Protection'
Rogue: 'Subetly' | 'Combat' | 'Assassination'
Hunter: 'Beast Mastery' | 'Marksmanship' | 'Survival'
Paladin: 'Holy' | 'Protection' | 'Retribution'
Priest: 'Discipline' | 'Holy' | 'Shadow'
Druid: 'Feral (Tank)' | 'Feral (DPS)' | 'Restoration' | 'Balance'
Mage: 'Frost' | 'Arcane' | 'Fire'
Warlock: 'Demonology' | 'Destruction' | 'Affliction'
Shaman: 'Restoration' | 'Enhancement' | 'Elemental'
}
export const specs : { [classname in keyof SpecT] : SpecT[classname][] } = {
Warrior : [
'Arms',
'Fury',
+5 -4
View File
@@ -1,8 +1,9 @@
import { FrontworkAdmin } from "../Admin/Admin"
import { RPC, RPCExporter } from "rpclibrary"
import { TableDefiniton } from "./Types"
import { TableDefinitionExporter } from "./Interfaces"
import { RPCExporter, RPC } from "rpclibrary"
import { ConfigExporter } from "loadson"
import { TableDefinitionExporter } from "./Interfaces"
import { FrontworkAdmin } from "../Admin/Admin"
import { TableDefiniton } from "./Types"
export abstract class Plugin<ConfType = {}>
implements ConfigExporter<ConfType>, RPCExporter<any,any,any>, TableDefinitionExporter{
+14 -6
View File
@@ -1,7 +1,7 @@
import * as Knex from "knex"
import { RPCExporter } from "rpclibrary";
import { RaidManagerFeatureIfc } from "../Components/Raid/RPCInterface";
import { LoginManagerIfc, LoginManagerFeatureIfc } from "../Components/User/RPCInterface";
import { RaidManagerIfc, RaidManagerFeatureIfc } from "../Components/Raid/RPCInterface";
import { LoginManagerIfc, CharacterManagerIfc, LoginManagerFeatureIfc, CharacterManagerFeatureIfc } from "../Components/User/RPCInterface";
export declare type NotificationSeverity = 'Info' | 'Important' | 'Error';
@@ -34,9 +34,9 @@ export type User = {
id?: number
name: string
pwhash: string
specid: number
rank: Rank
email?: string
locked: boolean
}
export type Raid = {
@@ -52,6 +52,13 @@ export type Signup = {
user_id: number
}
export type Character = {
id? : number
name : string
specid : number
userid : number
}
export type Token = {
value: string
user_id: number
@@ -60,12 +67,13 @@ export type Token = {
export type Auth = {port: number, user: User, token: Token}
export type FrontcraftIfc = LoginManagerIfc
//& ItemManagerIfc
export type FrontcraftIfc = RaidManagerIfc
& LoginManagerIfc
& CharacterManagerIfc
export type FrontcraftFeatureIfc = RaidManagerFeatureIfc
& LoginManagerFeatureIfc
//& ItemManagerFeatureIfc
& CharacterManagerFeatureIfc
export type Spec = {
id?: number,
+3 -3
View File
@@ -14269,9 +14269,9 @@
"integrity": "sha512-ZYzRkETgBrdEGzL5JSKimvjI2CX7ioyZCkX2BpcfyjqI+079W0wHAyj5W4rIZMcDSOHgLZtgz1IdDi/vU77KEQ=="
},
"rpclibrary": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.5.1.tgz",
"integrity": "sha512-EN6wlifkFmEQrHhtbalS0i90ZBNbLNFcrQ8hTrcJLBuNMFab49R8i5fruoDLDJM88iPcx6eWccI6zLHK7Qkl2A==",
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.6.2.tgz",
"integrity": "sha512-lQTU4XkB9CSHz7YgtAcpVfyR5XrmTRX4P4eQjK6DDUjqKSFvJb5ChXjHnt1BcaNWbJ2VqmhCNVbcoyunJ2u7Rg==",
"requires": {
"bsock": "^0.1.9",
"http": "0.0.0",
+1 -1
View File
@@ -69,7 +69,7 @@
"normalize.css": "6.0.0",
"pace-js": "1.0.2",
"roboto-fontface": "0.8.0",
"rpclibrary": "^1.5.1",
"rpclibrary": "^1.6.2",
"rxjs": "6.5.2",
"rxjs-compat": "6.3.0",
"socicon": "3.0.5",
+1 -3
View File
@@ -19,9 +19,7 @@ export class AppComponent implements OnInit {
ngOnInit(): void {
this.analytics.trackPageViews();
this.loginSvc.getFeature('createUser').then( (f) => {
})
window['s'] = this.loginSvc
}
}
@@ -1,6 +1,6 @@
<h1 id="title" class="title">Account application</h1>
<form (ngSubmit)="register()" #form="ngForm" aria-labelledby="title">
<form (ngSubmit)="onSubmit()" #form="ngForm" aria-labelledby="title">
<div class="form-control-group">
<label class="label" for="input-email">Email address:</label>
@@ -36,7 +36,7 @@
<label class="label" for="input-password">Password:</label>
<input nbInput
fullWidth
[(ngModel)]="user.password"
[(ngModel)]="user.pwhash"
#password="ngModel"
name="password"
type="password"
@@ -58,14 +58,30 @@
<nb-card-body>
<nb-checkbox [(ngModel)]="showApplication" name="amMember" #checkbox>I am already a member</nb-checkbox>
<br />
<span *ngIf="showApplication">
And my main is &nbsp; <input name="preMember" nbInput> with rank <nb-select placeholder="Rank">
<nb-option *ngFor="let rank of ranks" [value]=rank >{{rank}}</nb-option>
And my main is &nbsp; <input name="preMember" [(ngModel)]="character.name" nbInput>, a
<nb-select [(selected)]="character.spec" placeholder="Spec" (selectedChange)="onSelectSpec()">
<nb-option *ngFor="let spec of specs" [value]="spec">{{spec}}</nb-option>
</nb-select>
<nb-select [(selected)]="character.class" placeholder="Class" (selectedChange)="onSelectClass()">
<nb-option *ngFor="let class of classes" [value]="class">{{class}}</nb-option>
</nb-select>
with rank
<nb-select [(selected)]="user.rank" placeholder="Rank">
<nb-option *ngFor="let rank of ranks" [value]="rank" >{{rank}}</nb-option>
</nb-select>
</span>
<span *ngIf="!showApplication">Hello my name is &nbsp; <input name="charName" nbInput>
I am playing a level 60 <input name="charName" placeholder="Spec + Class" nbInput> and I would like to join tranquil because
<span *ngIf="!showApplication">Hello my name is &nbsp; <input name="charName" [(ngModel)]="character.name" nbInput>
I am playing a level 60
<nb-select [(selected)]="character.spec" placeholder="Spec" (selectedChange)="onSelectSpec()">
<nb-option *ngFor="let spec of specs" [value]="spec">{{spec}}</nb-option>
</nb-select>
<nb-select [(selected)]="character.class" placeholder="Class" (selectedChange)="onSelectClass()">
<nb-option *ngFor="let class of classes" [value]="class">{{class}}</nb-option>
</nb-select>
and I would like to join Tranquil because
<textarea nbInput fullWidth placeholder="reason" style="min-height: 400px"></textarea>
</span>
@@ -79,7 +95,7 @@
size="giant"
[disabled]="submitted || !form.valid"
[class.btn-pulse]="submitted">
Log In
Register
</button>
</form>
@@ -1,7 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { LoginApiService } from '../../services/login-api';
import { LoginApiService, hash } from '../../services/login-api';
import { Router } from '@angular/router';
import { _Rank } from '../../../../../../backend/Types/Types'
import { _Rank, _Class, Class, User } from '../../../../../../backend/Types/Types'
import { specs } from '../../../../../../backend/Types/PlayerSpecs'
@Component({
selector: 'register',
@@ -9,9 +11,22 @@ import { _Rank } from '../../../../../../backend/Types/Types'
})
export class RegisterComponent implements OnInit{
user: any = {};
showApplication = false
user = {
rank: "Guest"
} as User
character: any = {
class : "Warrior",
spec : "Arms"
}
ranks = _Rank
classes = _Class
selectedClass : Class = "Warrior"
selectedSpec = specs[this.selectedClass][0]
specs = specs[this.selectedClass]
showApplication = false
constructor(
private router : Router,
@@ -23,10 +38,32 @@ export class RegisterComponent implements OnInit{
if(loggedin){
this.router.navigateByUrl("/")
}
});
})
}
login(){
this.loginApi.login(this.user.name, this.user.password)
onSelectClass(){
this.specs = specs[this.character.class]
setTimeout(() => {
this.character.spec = this.specs[0]
}, 25)
}
onSelectSpec(){
}
async onSubmit(){
this.user.pwhash = await hash(this.user.pwhash)
const user = this.user
this.user = {} as User
const char = this.character
this.character = {}
try{
const usr = await this.loginApi.getUnprivilegedSocket().Authenticator.createUser(user)
}catch(e){
alert("Error creating user"+e)
}
}
}
@@ -65,11 +65,8 @@ export class LoginApiService{
getCurrentUser = () : User | undefined => this.auth?this.auth.user:undefined
login = async (username: string, password: string) : Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>> => {
const buf = str2arraybuf(password)
const pwHash = await crypto.subtle.digest('SHA-256', buf);
const auth = await this.socket.Authenticator.login(username, buf2hex(pwHash))
const pwHash = await hash(password)
const auth = await this.socket.Authenticator.login(username, pwHash)
if(!auth){
await this.logout()
@@ -129,6 +126,12 @@ function buf2hex(buffer) { // buffer is an ArrayBuffer
return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
}
export async function hash(value:string) : Promise<string>{
const buf = str2arraybuf(value)
const pwHash = await crypto.subtle.digest('SHA-256', buf);
return buf2hex(pwHash)
}
//angular depenency manager requires this
export function initializeLoginSvc(svc: LoginApiService): () => Promise<any> {
return svc.initialize
+2 -2
View File
@@ -9,12 +9,12 @@
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2015",
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"es2019",
"dom"
],
"plugins": [
+4 -3
View File
@@ -7,8 +7,9 @@
"declaration": true,
"outDir": "./lib",
"strict": true,
"experimentalDecorators": true
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": ["src/backend/**/*"],
"exclude": ["node_modules", "**/__tests__/*"],
"include": ["src/backend/**/*", "test/**/*"],
"exclude": ["node_modules", "**/__tests__/*"]
}