migrations

This commit is contained in:
peter
2020-03-25 23:49:54 +01:00
parent b28f8bc7ce
commit 9ff98d92ae
62 changed files with 711 additions and 483 deletions
Binary file not shown.
Binary file not shown.
+53 -35
View File
@@ -479,7 +479,8 @@
"bluebird": { "bluebird": {
"version": "3.5.5", "version": "3.5.5",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
"integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==" "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==",
"dev": true
}, },
"body-parser": { "body-parser": {
"version": "1.19.0", "version": "1.19.0",
@@ -835,9 +836,9 @@
"dev": true "dev": true
}, },
"colorette": { "colorette": {
"version": "1.0.8", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.0.8.tgz", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.1.0.tgz",
"integrity": "sha512-X6Ck90ReaF+EfKdVGB7vdIQ3dr651BbIrBwY5YBKg13fjH+940sTtp7/Pkx33C6ntYfQcRumOs/aUQhaRPpbTQ==" "integrity": "sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg=="
}, },
"combined-stream": { "combined-stream": {
"version": "1.0.8", "version": "1.0.8",
@@ -850,7 +851,8 @@
"commander": { "commander": {
"version": "2.20.0", "version": "2.20.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
"dev": true
}, },
"commondir": { "commondir": {
"version": "1.0.1", "version": "1.0.1",
@@ -2057,9 +2059,9 @@
"integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg="
}, },
"getopts": { "getopts": {
"version": "2.2.4", "version": "2.2.5",
"resolved": "https://registry.npmjs.org/getopts/-/getopts-2.2.4.tgz", "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.2.5.tgz",
"integrity": "sha512-Rz7DGyomZjrenu9Jx4qmzdlvJgvrEFHXHvjK0FcZtcTC1U5FmES7OdZHUwMuSnEE6QvBvwse1JODKj7TgbSEjQ==" "integrity": "sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA=="
}, },
"getpass": { "getpass": {
"version": "0.1.7", "version": "0.1.7",
@@ -2139,6 +2141,16 @@
} }
} }
}, },
"global-modules": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
"integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
"requires": {
"global-prefix": "^1.0.1",
"is-windows": "^1.0.1",
"resolve-dir": "^1.0.0"
}
},
"global-prefix": { "global-prefix": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
@@ -2731,27 +2743,37 @@
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
}, },
"knex": { "knex": {
"version": "0.19.2", "version": "0.19.5",
"resolved": "https://registry.npmjs.org/knex/-/knex-0.19.2.tgz", "resolved": "https://registry.npmjs.org/knex/-/knex-0.19.5.tgz",
"integrity": "sha512-TVYvlp2esS4LjjJSz8XuE48bPJq4N3lWnETQVgJ3hXPEqjiDjxcTa3bCn6F5ipQuBaMAAaFHNrqsZm7BttogdA==", "integrity": "sha512-Hy258avCVircQq+oj3WBqPzl8jDIte438Qlq+8pt1i/TyLYVA4zPh2uKc7Bx0t+qOpa6D42HJ2jjtl2vagzilw==",
"requires": { "requires": {
"bluebird": "^3.5.5", "bluebird": "^3.7.0",
"colorette": "1.0.8", "colorette": "1.1.0",
"commander": "^2.20.0", "commander": "^3.0.2",
"debug": "4.1.1", "debug": "4.1.1",
"getopts": "2.2.4", "getopts": "2.2.5",
"inherits": "~2.0.4", "inherits": "~2.0.4",
"interpret": "^1.2.0", "interpret": "^1.2.0",
"liftoff": "3.1.0", "liftoff": "3.1.0",
"lodash": "^4.17.15", "lodash": "^4.17.15",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"pg-connection-string": "2.0.0", "pg-connection-string": "2.1.0",
"tarn": "^2.0.0", "tarn": "^2.0.0",
"tildify": "2.0.0", "tildify": "2.0.0",
"uuid": "^3.3.2", "uuid": "^3.3.3",
"v8flags": "^3.1.3" "v8flags": "^3.1.3"
}, },
"dependencies": { "dependencies": {
"bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
},
"commander": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz",
"integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow=="
},
"inherits": { "inherits": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -3948,9 +3970,9 @@
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
}, },
"pg-connection-string": { "pg-connection-string": {
"version": "2.0.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.0.0.tgz", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.1.0.tgz",
"integrity": "sha1-Pu/lmX4G2Ugh5NUC5CtqHHP434I=" "integrity": "sha512-bhlV7Eq09JrRIvo1eKngpwuqKtJnNhZdpdOlvrPrA4dxqXPjxSrbNrfnIDmTpwMyRszrcV4kU5ZA4mMsQUrjdg=="
}, },
"picomatch": { "picomatch": {
"version": "2.2.1", "version": "2.2.1",
@@ -4340,18 +4362,6 @@
"requires": { "requires": {
"expand-tilde": "^2.0.0", "expand-tilde": "^2.0.0",
"global-modules": "^1.0.0" "global-modules": "^1.0.0"
},
"dependencies": {
"global-modules": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
"integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
"requires": {
"global-prefix": "^1.0.1",
"is-windows": "^1.0.1",
"resolve-dir": "^1.0.0"
}
}
} }
}, },
"resolve-url": { "resolve-url": {
@@ -4388,13 +4398,21 @@
} }
}, },
"rpclibrary": { "rpclibrary": {
"version": "1.9.2", "version": "1.10.2",
"resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.9.2.tgz", "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.10.2.tgz",
"integrity": "sha512-MOtVm0IBRLryXag1IwkYNPVFegbvW10+MbCjhr3GptO3MA7nHZ545ammgoCWnREtGOgFeN4Jib/EBDOJ9Ep41Q==", "integrity": "sha512-uf4FtylK+tyVo1O8YVihxYxaI4KHnKBzq05SsLYe8ZPXdO8Hqrv7Je5cwv3bfx0FBCO1vVrSc8DT1P6ypGY6uQ==",
"requires": { "requires": {
"bsock": "^0.1.9", "bsock": "^0.1.9",
"crypto-js": "^4.0.0",
"http": "0.0.0", "http": "0.0.0",
"uuid": "^3.3.3" "uuid": "^3.3.3"
},
"dependencies": {
"crypto-js": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
"integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg=="
}
} }
}, },
"run-queue": { "run-queue": {
+3 -2
View File
@@ -4,6 +4,7 @@
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {
"tsc": "tsc", "tsc": "tsc",
"knex": "knex",
"launch": "node lib/src/backend/Launcher.js", "launch": "node lib/src/backend/Launcher.js",
"start": "npm run build && npm run launch", "start": "npm run build && npm run launch",
"start-backend": "npm run build-backend && npm run launch", "start-backend": "npm run build-backend && npm run launch",
@@ -37,7 +38,6 @@
"git-cherrypicker": "0.0.3", "git-cherrypicker": "0.0.3",
"git-describe": "^4.0.4", "git-describe": "^4.0.4",
"http": "0.0.0", "http": "0.0.0",
"knex": "^0.19.2",
"loadson": "^1.0.0", "loadson": "^1.0.0",
"log4js": "^4.5.1", "log4js": "^4.5.1",
"lowdb": "^1.0.0", "lowdb": "^1.0.0",
@@ -46,7 +46,7 @@
"path": "^0.12.7", "path": "^0.12.7",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.0", "rimraf": "^3.0.0",
"rpclibrary": "^1.9.2", "rpclibrary": "^1.10.2",
"simple-git": "^1.124.0", "simple-git": "^1.124.0",
"spawn-sync": "^2.0.0", "spawn-sync": "^2.0.0",
"sqlite3": "^4.1.1", "sqlite3": "^4.1.1",
@@ -58,6 +58,7 @@
"xmlhttprequest": "^1.8.0" "xmlhttprequest": "^1.8.0"
}, },
"devDependencies": { "devDependencies": {
"knex": "^0.19.5",
"madge": "^3.6.0", "madge": "^3.6.0",
"mocha": "^7.1.0", "mocha": "^7.1.0",
"terser-webpack-plugin": "^1.4.1", "terser-webpack-plugin": "^1.4.1",
+71 -62
View File
@@ -4,7 +4,7 @@ import { getLogger, Logger, } from "log4js";
import { promises as fs, mkdirSync } from "fs" import { promises as fs, mkdirSync } from "fs"
import { RPCServer } from 'rpclibrary' import { RPCServer } from 'rpclibrary'
import * as Path from 'path' import * as Path from 'path'
import * as Knex from 'knex'; import * as Knex from 'knex';
import * as http from 'http'; import * as http from 'http';
import * as express from 'express'; import * as express from 'express';
import { GuildManager } from '../Components/Guild/GuildManager'; import { GuildManager } from '../Components/Guild/GuildManager';
@@ -36,18 +36,18 @@ getLogger().level = 'debug'
PubSub PubSub
] ]
}) })
export class FrontworkAdmin export class FrontworkAdmin
implements TableDefinitionExporter, IAdmin { implements TableDefinitionExporter, IAdmin {
knex:Knex knex: Knex
config: RPCConfigLoader<AdminConf> config: RPCConfigLoader<AdminConf>
rpcServer: RPCServer rpcServer: RPCServer
private express private express
private httpServer private httpServer
constructor(private frontworkComponents: FrontworkComponent[] = []){ constructor(private frontworkComponents: FrontworkComponent[] = []) {
this.config = new RPCConfigLoader<AdminConf>({ this.config = new RPCConfigLoader<AdminConf>({
name: "FrontworkAdminConf", name: "FrontworkAdminConf",
getDefaultConfig: () => { getDefaultConfig: () => {
return { return {
httpPort: 8080, httpPort: 8080,
@@ -55,57 +55,61 @@ implements TableDefinitionExporter, IAdmin {
dbConf: { dbConf: {
client: 'sqlite3', client: 'sqlite3',
connection: { connection: {
filename: Path.resolve(__dirname, '../../../..', "data/frontworkAdmin.sqlite") filename: Path.resolve(__dirname, '../../../..', "data/frontworkAdmin.sqlite"),
},
migrations: {
directory: Path.resolve(__dirname, '../../../..', "migrations"),
extension: 'ts'
}, },
useNullAsDefault: true, useNullAsDefault: true,
} }
} }
} }
}, './config', this.configChangeHandler) }, './config', this.configChangeHandler)
} }
async start(){ async start() {
await this.makeKnex() await this.makeKnex()
this.startWebsocket() this.startWebsocket()
await Promise.all( this.frontworkComponents.map(c => c.initialize?c.initialize():undefined )) await Promise.all(this.frontworkComponents.map(c => c.initialize ? c.initialize() : undefined))
getLogger('Admin#start').debug(this.frontworkComponents.length+" components initialized") getLogger('Admin#start').debug(this.frontworkComponents.length + " components initialized")
await this.startWebserver() await this.startWebserver()
} }
stop(){ stop() {
Promise.all([ Promise.all([
...this.frontworkComponents.map(c => c.stop?c.stop():undefined ), ...this.frontworkComponents.map(c => c.stop ? c.stop() : undefined),
]) ])
.catch(e => getLogger('Admin#stop').warn(e)) .catch(e => getLogger('Admin#stop').warn(e))
.finally(() => { .finally(() => {
this.rpcServer.destroy() this.rpcServer.destroy()
process.exit(0) process.exit(0)
}) })
} }
protected configChangeHandler = (conf:AdminConf, key?:string) => { protected configChangeHandler = (conf: AdminConf, key?: string) => {
if(key === 'dbConf'){ if (key === 'dbConf') {
this.makeKnex() this.makeKnex()
} }
} }
getConfigKey(key:string){ getConfigKey(key: string) {
return this.config.getConfigKey(key) return this.config.getConfigKey(key)
} }
setConfigKey(key:string, value:any){ setConfigKey(key: string, value: any) {
return this.config.setConfigKey(key, value) return this.config.setConfigKey(key, value)
} }
getTableDefinitions(): TableDefiniton[]{ getTableDefinitions(): TableDefiniton[] {
return [ return [
...this.frontworkComponents ...this.frontworkComponents
] ]
.filter(exp => exp.getTableDefinitions != null) .filter(exp => exp.getTableDefinitions != null)
.flatMap(exp => exp.getTableDefinitions()) .flatMap(exp => exp.getTableDefinitions())
} }
private startWebsocket(){ private startWebsocket() {
this.rpcServer = new RPCServer(20000, [ this.rpcServer = new RPCServer(20000, [
...this.frontworkComponents, ...this.frontworkComponents,
{ {
@@ -121,40 +125,41 @@ implements TableDefinitionExporter, IAdmin {
getLogger('Admin#startWebsocket').debug("Websocket up on", 20000) getLogger('Admin#startWebsocket').debug("Websocket up on", 20000)
} }
private async startWebserver(){ private async startWebserver() {
if(this.httpServer != null || this.express != null){ if (this.httpServer != null || this.express != null) {
getLogger('Admin#startWebserver').warn("Webserver is already running") getLogger('Admin#startWebserver').warn("Webserver is already running")
return return
} }
let port:number = this.config.getConfig().httpPort let port: number = this.config.getConfig().httpPort
this.express = express() this.express = express()
const distFolder = "../../../../dist" const distFolder = "../../../../dist"
const ngExpressServer = Path.join(__dirname, distFolder, 'server.js') const ngExpressServer = Path.join(__dirname, distFolder, 'server.js')
this.express.get('*.*', (req, res)=>{ this.express.get('*.*', (req, res) => {
//console.log('*.*', req.path); //console.log('*.*', req.path);
res.sendFile(Path.join(__dirname, distFolder,'browser',decodeURIComponent(req.path))) res.sendFile(Path.join(__dirname, distFolder, 'browser', decodeURIComponent(req.path)))
res.status(200)
}) })
try{ try {
const req = require(distFolder+"/server.js") const req = require(distFolder + "/server.js")
await req.attachExpress(this.express) await req.attachExpress(this.express)
getLogger('Admin#startWebserver').info('Frontend from '+ngExpressServer+" loaded") getLogger('Admin#startWebserver').info('Frontend from ' + ngExpressServer + " loaded")
}catch(e){ } catch (e) {
getLogger('Admin#startWebserver').error(e) getLogger('Admin#startWebserver').error(e)
getLogger('Admin#startWebserver').warn("No angular SSR module was provided in "+ngExpressServer) getLogger('Admin#startWebserver').warn("No angular SSR module was provided in " + ngExpressServer)
getLogger('Admin#startWebserver').warn("This is not fatal, but your page will not render on *"+port) getLogger('Admin#startWebserver').warn("This is not fatal, but your page will not render on *" + port)
} }
this.express.listen(port, () => { this.express.listen(port, () => {
getLogger('Admin#startWebserver').info('Admin panel listening for HTTP on *'+port) getLogger('Admin#startWebserver').info('Admin panel listening for HTTP on *' + port)
}) })
} }
private stopWebserver(){ private stopWebserver() {
if(this.httpServer == null || this.express == null){ if (this.httpServer == null || this.express == null) {
getLogger('Admin#stopWebserver').warn("Webserver is not running") getLogger('Admin#stopWebserver').warn("Webserver is not running")
return return
} }
@@ -164,38 +169,42 @@ implements TableDefinitionExporter, IAdmin {
getLogger('Admin#stopWebserver').info("Webserver stopped") getLogger('Admin#stopWebserver').info("Webserver stopped")
} }
async makeKnex():Promise<Knex>{ async makeKnex(): Promise<Knex> {
const conf:Knex.Config = this.config.getConfigKey("dbConf") const conf: Knex.Config = this.config.getConfigKey("dbConf")
getLogger('Admin#makeKnex').debug("Making new knex:", conf) getLogger('Admin#makeKnex').debug("Making new knex:", conf)
if(conf.client === 'sqlite3'){ if (conf.client === 'sqlite3') {
mkdirSync(Path.dirname((<any>conf.connection).filename), {recursive: true}) mkdirSync(Path.dirname((<any>conf.connection).filename), { recursive: true })
} }
this.knex = Knex(conf) this.knex = Knex(conf)
if(conf.client === 'sqlite3'){ if (conf.client === 'sqlite3') {
await this.knex.raw('PRAGMA foreign_keys = ON'); await this.knex.raw('PRAGMA foreign_keys = ON');
} }
await Promise.all( await Promise.all(
this.getTableDefinitions() this.getTableDefinitions()
//make unique by name //make unique by name
.filter((other, index, self) => index === self.findIndex( .filter((other, index, self) => index === self.findIndex(
(self) => self.name === other.name) (self) => self.name === other.name)
) )
//create table if not exists //create table if not exists
.map(async (def)=> { .map(async (def) => {
const hasTable = await this.knex.schema.hasTable(def.name) const hasTable = await this.knex.schema.hasTable(def.name)
if(!hasTable) if (!hasTable)
return await this.knex.schema.createTable(def.name, def.tableBuilder) return await this.knex.schema.createTable(def.name, def.tableBuilder)
}) })
) )
await this.knex.migrate.latest().catch(e => {
getLogger('Admin#makeKnex').error(e)
process.exit(-1)
})
return this.knex return this.knex
} }
} }
process.on( 'SIGINT', function() { process.on('SIGINT', function () {
getLogger('process#SIGINT').info("Shutting down from SIGINT (Ctrl-C)" ); getLogger('process#SIGINT').info("Shutting down from SIGINT (Ctrl-C)");
Injector.resolve<FrontworkAdmin>(FrontworkAdmin).stop() Injector.resolve<FrontworkAdmin>(FrontworkAdmin).stop()
}) })
+2 -2
View File
@@ -3,7 +3,7 @@ import { Inject, Injectable } from "../../Injector/ServiceDecorator";
import { ItemManagerFeatureIfc, ItemManagerIfc } from "./RPCInterface"; import { ItemManagerFeatureIfc, ItemManagerIfc } from "./RPCInterface";
import { FrontworkComponent } from "../../Types/FrontworkComponent"; import { FrontworkComponent } from "../../Types/FrontworkComponent";
import { TableDefinitionExporter } from "../../Types/Interfaces"; import { TableDefinitionExporter } from "../../Types/Interfaces";
import { TableDefiniton, Item, User, Character, SRToken, SRPriority, Spec, Signup, Raid, Stats } from "../../Types/Types"; import { TableDefiniton, Item, Character, SRToken, SRPriority, Spec, Signup, Raid, Stats } from "../../Types/Types";
import { IAdmin } from "../../Admin/Interface"; import { IAdmin } from "../../Admin/Interface";
import { IItemManager } from "./Interface"; import { IItemManager } from "./Interface";
import { IUserManager } from "../User/Interface"; import { IUserManager } from "../User/Interface";
@@ -171,7 +171,7 @@ export class ItemManager
table.boolean('hidden').defaultTo(false).notNullable() table.boolean('hidden').defaultTo(false).notNullable()
table.string('tooltip').notNullable() table.string('tooltip').notNullable()
table.enu('tier', _Tiers).notNullable() table.enu('tier', _Tiers).notNullable()
table.json('stats').notNullable() table.json('stats')
} }
}] }]
} }
+2 -3
View File
@@ -1,7 +1,6 @@
import { SubscriptionResponse } from "rpclibrary"
export class IPubSub<UpdateType>{ export class IPubSub<UpdateType>{
publish: (topic: string, p: UpdateType) => Promise<void> publish: (topic: string, p: UpdateType) => Promise<void>
subscribe: (topic:string, callback: (p:UpdateType)=>any) => Promise<SubscriptionResponse&{data:any}> subscribe: (topic:string, callback: (p:UpdateType)=>any) => Promise<string>
unsubscribe: (uuid: string) => Promise<void> unsubscribe: (uuid: string) => Promise<void>
} }
+6 -7
View File
@@ -1,6 +1,7 @@
import { Injectable } from "../../Injector/ServiceDecorator"; import { Injectable } from "../../Injector/ServiceDecorator";
import { SubscriptionResponse, makeSubResponse, RPCExporter } from "rpclibrary"; import { RPCExporter } from "rpclibrary";
import { IPubSub } from "./Interface"; import { IPubSub } from "./Interface";
const uuidv4 = require('uuid/v4')
@Injectable(IPubSub) @Injectable(IPubSub)
export class PubSub<UpdateType = any> export class PubSub<UpdateType = any>
@@ -34,13 +35,11 @@ implements RPCExporter{
})) }))
} }
subscribe = async (topic:string, callback:(p:UpdateType)=>any) : Promise<SubscriptionResponse<{topic: string}>> => { subscribe = async (topic:string, callback:(p:UpdateType)=>any) : Promise<string> => {
const resp = makeSubResponse({ const uuid = uuidv4()
topic: topic
})
if(!this.subs[topic]) this.subs[topic] = {} if(!this.subs[topic]) this.subs[topic] = {}
this.subs[topic][resp.uuid] = callback this.subs[topic][uuid] = callback
return resp return uuid
} }
private unsubscribe = async (uuid: string) => { private unsubscribe = async (uuid: string) => {
+1 -1
View File
@@ -6,7 +6,7 @@ export class IRaidManager{
addSignup: (signup: Signup) => Promise<Signup> addSignup: (signup: Signup) => Promise<Signup>
removeSignup: (signup: Signup) => Promise<any> removeSignup: (signup: Signup) => Promise<any>
getSignups: (raid:Raid) => Promise<(Signup & Character & Spec & User)[]> getSignups: (raid:Raid) => Promise<(Signup & Character & Spec & User)[]>
sign: (userToken: string, character:Character, raid:Raid, late:boolean) => Promise<any> sign: (userToken: string, character:Character, raid:Raid, late:boolean, memo?: string) => Promise<any>
unsign: (userToken: string, character:Character, raid:Raid,) => Promise<any> unsign: (userToken: string, character:Character, raid:Raid,) => Promise<any>
archiveRaid: (raid:Raid) => Promise<RaidData> archiveRaid: (raid:Raid) => Promise<RaidData>
getRaidData: (raid:Raid) => Promise<RaidData> getRaidData: (raid:Raid) => Promise<RaidData>
+26 -29
View File
@@ -7,9 +7,11 @@ import { IRaidManager } from "./Interface";
import { IUserManager } from "../User/Interface"; import { IUserManager } from "../User/Interface";
import { ICharacterManager } from "../Character/Interface"; import { ICharacterManager } from "../Character/Interface";
import { _Tiers } from "../../Types/Items"; import { _Tiers } from "../../Types/Items";
import { MAX_MEMO_LENGTH } from "../../Types/Constants";
import { IItemManager } from "../Item/Interface"; import { IItemManager } from "../Item/Interface";
import { ItemManager } from "../Item/ItemManager"; import { ItemManager } from "../Item/ItemManager";
import { IPubSub } from "../PubSub/Interface"; import { IPubSub } from "../PubSub/Interface";
import { getLogger } from "log4js";
@Injectable(IRaidManager) @Injectable(IRaidManager)
export class RaidManager export class RaidManager
@@ -89,6 +91,8 @@ export class RaidManager
table.foreign('characterid').references('id').inTable('characters').onDelete('CASCADE') table.foreign('characterid').references('id').inTable('characters').onDelete('CASCADE')
table.boolean('benched').defaultTo('false') table.boolean('benched').defaultTo('false')
table.boolean('late') table.boolean('late')
table.timestamp('timestamp').defaultTo(this.admin.knex.fn.now())
table.string('memo').nullable()
} }
} }
] ]
@@ -96,12 +100,20 @@ export class RaidManager
notifyRaid = async (raid:Raid | {id:number}) => { notifyRaid = async (raid:Raid | {id:number}) => {
const data = await this.getRaidData(<Raid>raid) const data = await this.getRaidData(<Raid>raid)
this.pubsub.publish(""+raid.id, data) try{
await this.notifyRaids() await this.pubsub.publish(String(raid.id), data)
await this.notifyRaids()
}catch(e){
getLogger('RaidManager#notifyRaid').debug(e);
}
} }
notifyRaids = async () => { notifyRaids = async () => {
this.pubsub.publish('raids', undefined) try{
await this.pubsub.publish('raids', undefined)
}catch(e){
getLogger('RaidManager#notifyRaids').debug(e);
}
} }
createRaid = async (raid: Raid): Promise<Raid> => { createRaid = async (raid: Raid): Promise<Raid> => {
@@ -158,7 +170,7 @@ export class RaidManager
await Promise.all([ await Promise.all([
...archived.participants.bench.map(giveCurrency), ...archived.participants.bench.map(giveCurrency),
...Object.values(archived.participants).flat().flatMap((b:Signup & Character & Spec) => giveCurrency(b)) ...Object.values(archived.participants).flat().map((b:Signup & Character & Spec) => giveCurrency(b))
]) ])
await this.notifyRaids() await this.notifyRaids()
@@ -169,10 +181,10 @@ export class RaidManager
archiveRaid = async (raid: Raid): Promise<RaidData> => { archiveRaid = async (raid: Raid): Promise<RaidData> => {
const raidData = await this.getRaidData(raid) const raidData = await this.getRaidData(raid)
//const tx = await this.admin.knex.transaction() const tx = await this.admin.knex.transaction()
await this.admin.knex('archive') await this.admin.knex('archive')
//.transacting(tx) .transacting(tx)
.insert({ .insert({
id: raidData.id, id: raidData.id,
raiddata: JSON.stringify(raidData) raiddata: JSON.stringify(raidData)
@@ -182,7 +194,7 @@ export class RaidManager
Object.values(raidData.participants).flat().flatMap((p: (Signup & Character & Spec)) => Object.values(raidData.participants).flat().flatMap((p: (Signup & Character & Spec)) =>
this.admin this.admin
.knex(raid.tier + 'tokens') .knex(raid.tier + 'tokens')
//.transacting(tx) .transacting(tx)
.where({ .where({
characterid: p.characterid, characterid: p.characterid,
signupid: null signupid: null
@@ -191,10 +203,10 @@ export class RaidManager
)) ))
await this.admin.knex('raids') await this.admin.knex('raids')
//.transacting(tx) .transacting(tx)
.where('id', '=', raid.id) .where('id', '=', raid.id)
.del() .del()
//await tx.commit() await tx.commit()
const row = await this.admin.knex('archive') const row = await this.admin.knex('archive')
@@ -242,7 +254,6 @@ export class RaidManager
healers: <(Signup & Character & Spec)[]>[], healers: <(Signup & Character & Spec)[]>[],
tanks: <(Signup & Character & Spec)[]>[] tanks: <(Signup & Character & Spec)[]>[]
} }
//const tx = await this.admin.knex.transaction()
const subQuery = this.admin const subQuery = this.admin
.knex('signups') .knex('signups')
@@ -257,14 +268,12 @@ export class RaidManager
const raidInDb: Raid = await this.admin.knex('raids') const raidInDb: Raid = await this.admin.knex('raids')
.select('*', subQuery) .select('*', subQuery)
//.transacting(tx)
.where('id', '=', raid.id) .where('id', '=', raid.id)
.first() .first()
const characterData: (Signup & Character & Spec)[] = await this.admin const characterData: (Signup & Character & Spec)[] = await this.admin
.knex('signups as s') .knex('signups as s')
//.transacting(tx) .select('s.id as id', 'charactername', 'rank', 'class', 'specid', 'specname', 'race', 'userid', 'benched', 'late', 'raidid', 'characterid', 'specid', 'memo', 'timestamp')
.select('s.id as id', 'charactername', 'rank', 'class', 'specid', 'specname', 'race', 'userid', 'benched', 'late', 'raidid', 'characterid', 'specid')
.join('raids as r', 's.raidid', '=', 'r.id') .join('raids as r', 's.raidid', '=', 'r.id')
.join('characters as c', 's.characterid', '=', 'c.id') .join('characters as c', 's.characterid', '=', 'c.id')
.join('users as u', 'c.userid', '=', 'u.id') .join('users as u', 'c.userid', '=', 'u.id')
@@ -285,7 +294,6 @@ export class RaidManager
const tokenData: (Character & SRToken & Item)[] = await this.admin const tokenData: (Character & SRToken & Item)[] = await this.admin
.knex('signups as s') .knex('signups as s')
//.transacting(tx)
.select('*', 's.id as id') .select('*', 's.id as id')
.join('raids as r', 's.raidid', '=', 'r.id') .join('raids as r', 's.raidid', '=', 'r.id')
.join('characters as c', 's.characterid', '=', 'c.id') .join('characters as c', 's.characterid', '=', 'c.id')
@@ -300,8 +308,6 @@ export class RaidManager
this.whereNotNull('t.signupid') this.whereNotNull('t.signupid')
}) })
//await tx.commit()
tokenData.forEach(data => { tokenData.forEach(data => {
if (!raiddata.tokens[data.itemname]) if (!raiddata.tokens[data.itemname])
raiddata.tokens[data.itemname] = [] raiddata.tokens[data.itemname] = []
@@ -338,16 +344,15 @@ export class RaidManager
.select('*', 'si.id as id') .select('*', 'si.id as id')
.where('raidid', '=', raid.id!) .where('raidid', '=', raid.id!)
sign = async (usertoken: string, character: Character, raid: Raid, late: boolean) => { sign = async (usertoken: string, character: Character, raid: Raid, late: boolean, memo?: string) => {
const maybeUserRecord = this.userManager.getUserRecordByToken(usertoken) const maybeUserRecord = this.userManager.getUserRecordByToken(usertoken)
if (!maybeUserRecord || maybeUserRecord.user.id != character.userid) { if (!maybeUserRecord || maybeUserRecord.user.id != character.userid) {
throw new Error("Bad Usertoken") throw new Error("Bad Usertoken")
} }
//const tx = await this.admin.knex.transaction()
if(memo) memo = memo.substring(0, MAX_MEMO_LENGTH)
const exists = await this.admin const exists = await this.admin
.knex('signups') .knex('signups')
//.transacting(tx)
.select('*') .select('*')
.where({ .where({
raidid: raid.id!, raidid: raid.id!,
@@ -358,17 +363,16 @@ export class RaidManager
if (!exists) { if (!exists) {
await this.admin await this.admin
.knex('signups') .knex('signups')
//.transacting(tx)
.insert({ .insert({
raidid: raid.id!, raidid: raid.id!,
characterid: character.id!, characterid: character.id!,
late: late, late: late,
benched: false, benched: false,
memo: memo
}) })
} else { } else {
await this.admin await this.admin
.knex('signups') .knex('signups')
//.transacting(tx)
.where({ .where({
id: exists.id id: exists.id
}) })
@@ -377,9 +381,9 @@ export class RaidManager
characterid: character.id!, characterid: character.id!,
late: late, late: late,
benched: false, benched: false,
memo: memo
}) })
} }
//await tx.commit()
await this.notifyRaid(raid) await this.notifyRaid(raid)
@@ -405,13 +409,6 @@ export class RaidManager
adminUnsign = async (character: Character, raid: Raid) => { adminUnsign = async (character: Character, raid: Raid) => {
const user = await this.characterManager.getUserOfCharacter(character) const user = await this.characterManager.getUserOfCharacter(character)
const signup = await this.admin.knex('signups as si')
.where({
"si.raidid": raid.id!,
"si.characterid": character.id!,
}).first()
const tokens = await this.itemManager.getTokens(character, [raid.tier], true) const tokens = await this.itemManager.getTokens(character, [raid.tier], true)
//check if token has to be deleted //check if token has to be deleted
+1 -2
View File
@@ -1,5 +1,4 @@
import { Raid, Signup, Character, RaidData } from "../../Types/Types" import { Raid, Signup, Character, RaidData } from "../../Types/Types"
import { SubscriptionResponse } from "rpclibrary"
export type ShoutMessage = { export type ShoutMessage = {
message: string, message: string,
@@ -10,5 +9,5 @@ export type ShoutMessage = {
export class IShoutbox{ export class IShoutbox{
shout: (uuid:string, msg: ShoutMessage) => Promise<void> shout: (uuid:string, msg: ShoutMessage) => Promise<void>
getFeed: () => Promise<ShoutMessage[]> getFeed: () => Promise<ShoutMessage[]>
subscribe: (callback) => Promise<SubscriptionResponse> subscribe: (callback) => Promise<string>
} }
+5 -5
View File
@@ -3,8 +3,8 @@ import { ShoutboxFeatureIfc, ShoutboxIfc } from "./RPCInterface";
import { FrontworkComponent } from "../../Types/FrontworkComponent"; import { FrontworkComponent } from "../../Types/FrontworkComponent";
import { TableDefiniton } from "../../Types/Types"; import { TableDefiniton } from "../../Types/Types";
import { IShoutbox, ShoutMessage } from "./Interface"; import { IShoutbox, ShoutMessage } from "./Interface";
import { SubscriptionResponse, makeSubResponse } from "rpclibrary";
import * as CircularBuffer from "circular-buffer"; import * as CircularBuffer from "circular-buffer";
const uuid = require('uuid/v4')
@Injectable(IShoutbox) @Injectable(IShoutbox)
export class Shoutbox export class Shoutbox
@@ -52,10 +52,10 @@ implements FrontworkComponent<ShoutboxIfc, ShoutboxFeatureIfc>, IShoutbox{
return this.log.toarray() return this.log.toarray()
} }
subscribe = async (callback) : Promise<SubscriptionResponse> => { subscribe = async (callback) : Promise<string> => {
const resp = makeSubResponse({}) uuid()
this.subs[resp.uuid] = callback this.subs[uuid] = callback
return resp return uuid
} }
unsubscribe = async (uuid: string) => { unsubscribe = async (uuid: string) => {
+7 -7
View File
@@ -6,10 +6,10 @@ import { RaidManager } from "../Raid/RaidManager";
import { CharacterManager } from "../Character/CharacterManager"; import { CharacterManager } from "../Character/CharacterManager";
import { UserManagerFeatureIfc, UserManagerIfc } from "./RPCInterface"; import { UserManagerFeatureIfc, UserManagerIfc } from "./RPCInterface";
import { FrontworkComponent } from "../../Types/FrontworkComponent"; import { FrontworkComponent } from "../../Types/FrontworkComponent";
import { Rank, User, Auth, _Rank, TableDefiniton, RPCPermission, FrontcraftFeatureIfc, AnyRPCExporter, Token, UserRecord } from "../../Types/Types"; import { Rank, User, Auth, _Rank, TableDefiniton, RPCPermission, FrontcraftFeatureIfc, Token, UserRecord } from "../../Types/Types";
import { IAdmin } from "../../Admin/Interface"; import { IAdmin } from "../../Admin/Interface";
import { IUserManager } from "./Interface"; import { IUserManager } from "./Interface";
import { getLogger, Logger } from "log4js"; import { getLogger } from "log4js";
import { saltedHash } from "../../Util/hash"; import { saltedHash } from "../../Util/hash";
import { _Tiers, Tiers } from "../../Types/Items"; import { _Tiers, Tiers } from "../../Types/Items";
import { ICharacterManager } from "../Character/Interface"; import { ICharacterManager } from "../Character/Interface";
@@ -24,8 +24,8 @@ const uuid = require('uuid/v4')
const salt = "6pIbc6yjSN" const salt = "6pIbc6yjSN"
const ONE_WEEK = 604800000 const ONE_WEEK = 604800000
type Serverstate<SubresT, InterfaceT extends RPCInterface> = { type Serverstate<InterfaceT extends RPCInterface> = {
server: RPCServer<SubresT, InterfaceT>, server: RPCServer<InterfaceT>,
port: number, port: number,
}; };
@@ -51,7 +51,7 @@ export class UserManager
private character: CharacterManager private character: CharacterManager
exporters: any[] = [] exporters: any[] = []
rankServer: Serverstate<{}, FrontcraftFeatureIfc> rankServer: Serverstate<FrontcraftFeatureIfc>
userLogins: { [username in string]: UserRecord } = {} userLogins: { [username in string]: UserRecord } = {}
allowed: string[] = [] allowed: string[] = []
@@ -376,12 +376,12 @@ export class UserManager
return return
} }
startRankServer = async (port: number): Promise<RPCServer<{}, FrontcraftFeatureIfc>> => { startRankServer = async (port: number): Promise<RPCServer<FrontcraftFeatureIfc>> => {
let rpcs = [ let rpcs = [
...this.exportRPCFeatures(), ...this.exportRPCFeatures(),
...this.exporters.flatMap((exp) => exp.exportRPCFeatures()) ...this.exporters.flatMap((exp) => exp.exportRPCFeatures())
] ]
let rpcServer = new RPCServer<{}, FrontcraftFeatureIfc>(port, rpcs, { let rpcServer = new RPCServer<FrontcraftFeatureIfc>(port, rpcs, {
accessFilter: async (sesame, exporter) => { accessFilter: async (sesame, exporter) => {
const record = this.getUserRecordByToken(sesame!) const record = this.getUserRecordByToken(sesame!)
if (!record) return false if (!record) return false
-1
View File
@@ -1,6 +1,5 @@
import { Injector } from "./Injector"; import { Injector } from "./Injector";
import { Type, GenericClassDecorator } from "./Util"; import { Type, GenericClassDecorator } from "./Util";
import { FrontworkComponent } from "../Types/FrontworkComponent";
import { RPCExporter } from "rpclibrary"; import { RPCExporter } from "rpclibrary";
/** /**
+1
View File
@@ -0,0 +1 @@
export const MAX_MEMO_LENGTH = 255
+4 -5
View File
@@ -1,4 +1,4 @@
import { RPCInterface, RPCExporter, RPCInterfaceArray } from "rpclibrary"; import { RPCInterface, RPCExporter, RPCDefinitions } from "rpclibrary";
import { PrivilegedRPCExporter } from "./PrivilegedRPCExporter"; import { PrivilegedRPCExporter } from "./PrivilegedRPCExporter";
import { TableDefinitionExporter } from "./Interfaces"; import { TableDefinitionExporter } from "./Interfaces";
import { TableDefiniton } from "./Types"; import { TableDefiniton } from "./Types";
@@ -8,15 +8,14 @@ export interface FrontworkComponent<
FeatureIfc extends RPCInterface = RPCInterface, FeatureIfc extends RPCInterface = RPCInterface,
Name extends keyof Ifc = keyof Ifc, Name extends keyof Ifc = keyof Ifc,
FeatureName extends keyof FeatureIfc = keyof FeatureIfc, FeatureName extends keyof FeatureIfc = keyof FeatureIfc,
SubresT = {}
> extends > extends
PrivilegedRPCExporter<Ifc, FeatureIfc, Name, FeatureName, SubresT>, PrivilegedRPCExporter<Ifc, FeatureIfc, Name, FeatureName>,
TableDefinitionExporter TableDefinitionExporter
{ {
name: Name; name: Name;
exportRPCFeatures(): RPCExporter<FeatureIfc, FeatureName, SubresT>[] exportRPCFeatures(): RPCExporter<FeatureIfc, FeatureName>[]
exportRPCs(): RPCInterfaceArray<Ifc>[Name] exportRPCs(): RPCDefinitions<Ifc>[Name]
getTableDefinitions(): TableDefiniton[] getTableDefinitions(): TableDefiniton[]
initialize?(): Promise<any> initialize?(): Promise<any>
+1 -1
View File
@@ -6,7 +6,7 @@ import { TableDefiniton } from "./Types"
export abstract class Plugin<ConfType = {}> export abstract class Plugin<ConfType = {}>
implements ConfigExporter<ConfType>, RPCExporter<any,any,any>, TableDefinitionExporter{ implements ConfigExporter<ConfType>, RPCExporter, TableDefinitionExporter{
constructor (protected admin: FrontworkAdmin, public name:string){ constructor (protected admin: FrontworkAdmin, public name:string){
if(!this.getConfig()){ if(!this.getConfig()){
+2 -3
View File
@@ -1,11 +1,10 @@
import { RPCExporter, RPCInterface, RPC, RPCInterfaceArray } from "rpclibrary"; import { RPCExporter, RPCInterface } from "rpclibrary";
export interface PrivilegedRPCExporter < export interface PrivilegedRPCExporter <
Ifc extends RPCInterface = RPCInterface, Ifc extends RPCInterface = RPCInterface,
FeatureIfc extends RPCInterface = RPCInterface, FeatureIfc extends RPCInterface = RPCInterface,
Name extends keyof Ifc = keyof Ifc, Name extends keyof Ifc = keyof Ifc,
FeatureName extends keyof FeatureIfc = keyof FeatureIfc, FeatureName extends keyof FeatureIfc = keyof FeatureIfc,
SubresT = {}
>extends RPCExporter<Ifc, Name>{ >extends RPCExporter<Ifc, Name>{
exportRPCFeatures() : RPCExporter<FeatureIfc, FeatureName, SubresT>[] exportRPCFeatures() : RPCExporter<FeatureIfc, FeatureName>[]
} }
+2
View File
@@ -165,6 +165,8 @@ export type Signup = {
characterid: number characterid: number
benched: boolean benched: boolean
late: boolean late: boolean
timestamp: string
memo?: string
} }
export type Character = { export type Character = {
+3
View File
@@ -58,6 +58,9 @@
"configurations": { "configurations": {
"production": { "production": {
"aot": true, "aot": true,
"optimization": true,
"extractCss": true,
"sourceMap": true,
"fileReplacements": [ "fileReplacements": [
{ {
"replace": "src/environments/environment.ts", "replace": "src/environments/environment.ts",
+11 -3
View File
@@ -15742,13 +15742,21 @@
"integrity": "sha512-ZYzRkETgBrdEGzL5JSKimvjI2CX7ioyZCkX2BpcfyjqI+079W0wHAyj5W4rIZMcDSOHgLZtgz1IdDi/vU77KEQ==" "integrity": "sha512-ZYzRkETgBrdEGzL5JSKimvjI2CX7ioyZCkX2BpcfyjqI+079W0wHAyj5W4rIZMcDSOHgLZtgz1IdDi/vU77KEQ=="
}, },
"rpclibrary": { "rpclibrary": {
"version": "1.9.2", "version": "1.10.2",
"resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.9.2.tgz", "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.10.2.tgz",
"integrity": "sha512-MOtVm0IBRLryXag1IwkYNPVFegbvW10+MbCjhr3GptO3MA7nHZ545ammgoCWnREtGOgFeN4Jib/EBDOJ9Ep41Q==", "integrity": "sha512-uf4FtylK+tyVo1O8YVihxYxaI4KHnKBzq05SsLYe8ZPXdO8Hqrv7Je5cwv3bfx0FBCO1vVrSc8DT1P6ypGY6uQ==",
"requires": { "requires": {
"bsock": "^0.1.9", "bsock": "^0.1.9",
"crypto-js": "^4.0.0",
"http": "0.0.0", "http": "0.0.0",
"uuid": "^3.3.3" "uuid": "^3.3.3"
},
"dependencies": {
"crypto-js": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
"integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg=="
}
} }
}, },
"run-async": { "run-async": {
+3 -2
View File
@@ -29,7 +29,8 @@
"prepush": "npm run lint:ci", "prepush": "npm run lint:ci",
"release:changelog": "npm run conventional-changelog -- -p angular -i CHANGELOG.md -s", "release:changelog": "npm run conventional-changelog -- -p angular -i CHANGELOG.md -s",
"add": "ng add @nguniversal/express-engine", "add": "ng add @nguniversal/express-engine",
"tsc": "tsc --project src/tsconfig.server.json && webpack --config extra-webpack.config.js" "tsc": "tsc --project src/tsconfig.server.json && webpack --config extra-webpack.config.js",
"knex": "knex"
}, },
"dependencies": { "dependencies": {
"@agm/core": "^1.0.0-beta.5", "@agm/core": "^1.0.0-beta.5",
@@ -75,7 +76,7 @@
"normalize.css": "6.0.0", "normalize.css": "6.0.0",
"pace-js": "1.0.2", "pace-js": "1.0.2",
"roboto-fontface": "0.8.0", "roboto-fontface": "0.8.0",
"rpclibrary": "^1.9.2", "rpclibrary": "^1.10.2",
"rxjs": "6.5.2", "rxjs": "6.5.2",
"rxjs-compat": "6.3.0", "rxjs-compat": "6.3.0",
"socicon": "3.0.5", "socicon": "3.0.5",
+8 -3
View File
@@ -13,7 +13,7 @@ global['document'] = win.document;
global['alert'] = console.log global['alert'] = console.log
global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest; global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest;
export async function attachExpress(app, staticDir = "./dist") { export async function attachExpress(app, staticDir = "./dist", loggerService = console) {
const STATIC_FOLDER = resolve(process.cwd(), staticDir); const STATIC_FOLDER = resolve(process.cwd(), staticDir);
enableProdMode(); enableProdMode();
@@ -21,7 +21,7 @@ export async function attachExpress(app, staticDir = "./dist") {
const bundle = require(staticDir + '/server/main'); const bundle = require(staticDir + '/server/main');
const ServerApiService = bundle.ServerApiService const ServerApiService = bundle.ServerApiService
const serviceObj = new ServerApiService() const serviceObj = new ServerApiService(loggerService)
await serviceObj.initialize() await serviceObj.initialize()
app.set('view engine', 'html'); app.set('view engine', 'html');
@@ -31,10 +31,15 @@ export async function attachExpress(app, staticDir = "./dist") {
bootstrap: bundle.AppServerModuleNgFactory, bootstrap: bundle.AppServerModuleNgFactory,
providers: [ providers: [
provideModuleMap(bundle.LAZY_MODULE_MAP), provideModuleMap(bundle.LAZY_MODULE_MAP),
{
provide: bundle.LoggerService,
deps: [],
useValue: loggerService
},
{ {
provide: bundle.IApiService, provide: bundle.IApiService,
deps: [],
useValue: serviceObj, useValue: serviceObj,
deps: []
} }
] ]
}) })
@@ -29,7 +29,8 @@
</nb-action> </nb-action>
<nb-action class="user-action"> <nb-action class="user-action">
<nb-user [nbContextMenu]="userMenu" <nb-user style="text-transform: capitalize;"
[nbContextMenu]="userMenu"
[onlyPicture]="false" [onlyPicture]="false"
[name]="user?.username"> [name]="user?.username">
</nb-user> </nb-user>
@@ -5,7 +5,7 @@ import { LayoutService } from '../../../@core/utils';
import { map, takeUntil } from 'rxjs/operators'; import { map, takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { User } from '../../../../../../backend/Types/Types'; import { User } from '../../../../../../backend/Types/Types';
import { Router } from '@angular/router'; import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
import { ChatComponent } from './chat.component'; import { ChatComponent } from './chat.component';
import { ShoutMessage } from '../../../../../../backend/Components/Shoutbox/Interface'; import { ShoutMessage } from '../../../../../../backend/Components/Shoutbox/Interface';
import { IApiService } from '../../../frontcraft/services/ApiService'; import { IApiService } from '../../../frontcraft/services/ApiService';
@@ -41,7 +41,7 @@ export class HeaderComponent implements OnInit, OnDestroy {
]; ];
currentTheme = 'default'; currentTheme = 'default';
userMenu : NbMenuItem[] = [ { title: 'Log out', link: '/auth/logout' } ]; userMenu: NbMenuItem[] = [];
sendMessage: any = console.log sendMessage: any = console.log
chatwindow: ChatComponent chatwindow: ChatComponent
@@ -50,13 +50,29 @@ export class HeaderComponent implements OnInit, OnDestroy {
lastmessage = "asdasd" lastmessage = "asdasd"
modifyPermissions modifyPermissions
constructor(private sidebarService: NbSidebarService, constructor(
private router: Router, private sidebarService: NbSidebarService,
private themeService: NbThemeService, private router: Router,
private layoutService: LayoutService, private themeService: NbThemeService,
private api: IApiService, private layoutService: LayoutService,
private dialogService : NbDialogService private api: IApiService,
) {} private dialogService: NbDialogService,
private route: ActivatedRoute
) { }
private setUserMenu = (redirect = this.router.url) => {
if (this.user.username === 'Guest' && this.user.rank === 'Guest'){
this.userMenu = [
{ title: 'Login', link: '/auth/login', queryParams: { redirect: redirect } },
{ title: 'Register', link: '/auth/register', queryParams: { redirect: redirect } },
]
}else{
this.userMenu = [
{ title: 'Profile', link: '/frontcraft/user/' + this.user.username },
{ title: 'Log out', link: '/auth/logout', queryParams: { redirect: redirect } },
]
}
}
ngOnInit() { ngOnInit() {
this.themeService.changeTheme("dark"); this.themeService.changeTheme("dark");
@@ -64,28 +80,36 @@ export class HeaderComponent implements OnInit, OnDestroy {
this.currentTheme = this.themeService.currentTheme; this.currentTheme = this.themeService.currentTheme;
this.modifyPermissions = this.api.get('modifyPermissions') this.modifyPermissions = this.api.get('modifyPermissions')
this.user = this.api.getCurrentUser() this.user = this.api.getCurrentUser()
if(this.user)
this.userMenu.unshift({ title: 'Profile', link: '/frontcraft/user/'+this.user.username }); this.setUserMenu()
this.router.events.subscribe((val) => {
if(val instanceof NavigationEnd) {
this.setUserMenu(val.urlAfterRedirects)
}
})
this.api.get('GuildManager').getGuildInfo().then(info => { this.api.get('GuildManager').getGuildInfo().then(info => {
this.title = info.name this.title = info.name
}) })
this.api.connectShoutbox((msg) => { this.api.connectShoutbox((msg) => {
this.chatlog.push(msg) this.chatlog.push(msg)
msg['reply']=false msg['reply'] = false
if(msg.message != this.lastmessage){ if (msg.message != this.lastmessage) {
this.newmessage = true this.newmessage = true
msg['reply']=true msg['reply'] = true
} }
if(this.chatwindow) this.chatwindow.messages.push(msg) if (this.chatwindow) this.chatwindow.messages.push(msg)
}).then(sendMsg => { }).then(sendMsg => {
this.sendMessage = (msg) => { this.sendMessage = (msg) => {
sendMsg(msg) sendMsg(msg)
this.lastmessage = msg.message this.lastmessage = msg.message
} }
this.api.get('Shoutbox').getFeed().then(log => {this.chatlog = log}) this.api.get('Shoutbox').getFeed().then(log => { this.chatlog = log })
}) })
this.themeService.onThemeChange() this.themeService.onThemeChange()
@@ -96,14 +120,14 @@ export class HeaderComponent implements OnInit, OnDestroy {
.subscribe(themeName => this.currentTheme = themeName); .subscribe(themeName => this.currentTheme = themeName);
} }
openChat(){ openChat() {
this.newmessage = false this.newmessage = false
const ref = this.dialogService.open(ChatComponent, { const ref = this.dialogService.open(ChatComponent, {
context: { context: {
sendMessage: this.sendMessage, sendMessage: this.sendMessage,
} }
}); });
ref.onClose.subscribe(()=>{ ref.onClose.subscribe(() => {
this.chatwindow = null this.chatwindow = null
}) })
this.chatwindow = ref.componentRef.instance this.chatwindow = ref.componentRef.instance
@@ -122,7 +146,6 @@ export class HeaderComponent implements OnInit, OnDestroy {
toggleSidebar(): boolean { toggleSidebar(): boolean {
this.sidebarService.toggle(true, 'menu-sidebar'); this.sidebarService.toggle(true, 'menu-sidebar');
this.layoutService.changeLayoutSize(); this.layoutService.changeLayoutSize();
return false; return false;
} }
+1 -6
View File
@@ -9,9 +9,4 @@ import { Component, OnInit } from '@angular/core';
selector: 'ngx-app', selector: 'ngx-app',
template: '<router-outlet></router-outlet>', template: '<router-outlet></router-outlet>',
}) })
export class AppComponent implements OnInit { export class AppComponent { }
constructor() { }
ngOnInit(): void { }
}
+5 -5
View File
@@ -5,11 +5,15 @@
*/ */
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule, APP_INITIALIZER, NgZone, Injector } from '@angular/core'; import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { ThemeModule } from './@theme/theme.module'; import { ThemeModule } from './@theme/theme.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { CoreModule } from './@core/core.module';
import { FrontcraftPagesModule } from './frontcraft/pages/pages.module';
import { MyAuthModule } from './frontcraft/auth/auth.module';
import { PermissionsModule } from './frontcraft/permissions/permissions.module';
import { import {
NbChatModule, NbChatModule,
NbDatepickerModule, NbDatepickerModule,
@@ -19,10 +23,6 @@ import {
NbToastrModule, NbToastrModule,
NbWindowModule, NbWindowModule,
} from '@nebular/theme'; } from '@nebular/theme';
import { CoreModule } from './@core/core.module';
import { FrontcraftPagesModule } from './frontcraft/pages/pages.module';
import { MyAuthModule } from './frontcraft/auth/auth.module';
import { PermissionsModule } from './frontcraft/permissions/permissions.module';
@NgModule({ @NgModule({
declarations: [ declarations: [
+1 -1
View File
@@ -1,5 +1,5 @@
import { NgModule, APP_INITIALIZER } from '@angular/core'; import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server'; import { ServerModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader'; import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
@@ -0,0 +1,57 @@
import { Router, NavigationStart } from '@angular/router'
import { IApiService } from './services/ApiService'
import { AnyFunction } from 'rpclibrary'
import { LoggerService } from './services/logger.service'
import { OnDestroy } from '@angular/core'
export abstract class UpdatingComponent implements OnDestroy {
private _uuid: string
private _routerCancel: Function
constructor(
private _router: Router,
private _api: IApiService,
private _logger: LoggerService
) { }
protected subscribe = (
topic: string,
callback: AnyFunction,
) => {
const sub = this._router.events.subscribe(event => {
if (event instanceof NavigationStart) {
this.ngOnDestroy()
}
})
this._routerCancel = () => { sub.unsubscribe() }
this._api.get('PubSub').subscribe(topic, callback).then(uuid => {
this._logger.collapsed(`New subscription ${topic}`, "table", {
topic: topic,
uuid: uuid
})
this._uuid = uuid
}).catch(e => {
this._logger.warn(`Subscribe for ${topic} failed, ${String(e)}`)
delete this._uuid
})
}
//Note angular may call this function several times
ngOnDestroy() {
if (this._uuid) {
const id = this._uuid
delete this._uuid
this._api.get('PubSub').unsubscribe(id).then(() => {
this._logger.log(`Unsubscribed from ${id}`)
})
}
if (this._routerCancel) {
try {
this._routerCancel()
} catch (e) {
this._logger.warn(e)
}
delete this._routerCancel
}
}
}
@@ -1,6 +1,4 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ClientApiService } from '../services/client-login-api';
import { Router } from '@angular/router';
@Component({ @Component({
selector: 'auth-layout', selector: 'auth-layout',
@@ -48,5 +48,5 @@
<section class="another-action" aria-label="Register"> <section class="another-action" aria-label="Register">
Don't have an account? <a class="text-link" routerLink="/auth/register">Register</a> Don't have an account? <a class="text-link" routerLink="/auth/register" [queryParams]="{redirect: '/frontcraft'}">Register</a>
</section> </section>
@@ -1,6 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ClientApiService } from '../../services/client-login-api'; import { Router, ActivatedRoute } from '@angular/router';
import { Router } from '@angular/router';
import { IApiService } from '../../services/ApiService'; import { IApiService } from '../../services/ApiService';
import { NbToastrService } from '@nebular/theme'; import { NbToastrService } from '@nebular/theme';
@@ -24,7 +23,8 @@ export class MyLoginComponent implements OnInit{
constructor( constructor(
private router : Router, private router : Router,
private api : IApiService, private api : IApiService,
private toastr: NbToastrService private toastr: NbToastrService,
private route: ActivatedRoute
){} ){}
ngOnInit(){ ngOnInit(){
@@ -41,8 +41,9 @@ export class MyLoginComponent implements OnInit{
this.toastr.danger("Login failed", "Error") this.toastr.danger("Login failed", "Error")
return return
}else{ }else{
window['r'] = this.router this.route.queryParamMap.subscribe(params => {
this.router.navigate(["/froncraft"]) this.router.navigate([params.get('redirect')])
})
} }
} }
} }
@@ -1 +1 @@
You will be redirected shortly. If not click here <a href="/auth/login">click</a> You will be redirected shortly. If not click here <a routerLink="/auth/login" [queryParams]="{redirect: '/frontcraft'}">click</a>
@@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { IApiService } from '../../services/ApiService'; import { IApiService } from '../../services/ApiService';
import { Router } from '@angular/router'; import { Router, ActivatedRoute } from '@angular/router';
@Component({ @Component({
selector: 'mylogout', selector: 'mylogout',
@@ -10,12 +10,15 @@ export class LogoutComponent implements OnInit{
constructor( constructor(
private api : IApiService, private api : IApiService,
private router: Router private router: Router,
private route: ActivatedRoute
){} ){}
ngOnInit(){ ngOnInit(){
this.api.logout().then(() => { this.api.logout().then(() => {
this.router.navigate(['/auth/login']) this.route.queryParamMap.subscribe(params => {
this.router.navigate([params.get('redirect')])
})
}) })
} }
} }
@@ -92,5 +92,5 @@
<section class="another-action" aria-label="Register"> <section class="another-action" aria-label="Register">
Back to <a class="text-link" routerLink="auth/login">Login</a> Go to <a class="text-link" routerLink="auth/login" [queryParams]="{redirect: '/frontcraft'}">Login</a>
</section> </section>
@@ -1,9 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ClientApiService as ClientApiService } from '../../services/client-login-api'; import { Router, ActivatedRoute } from '@angular/router';
import { Router } from '@angular/router';
import { _Rank, _Class, Class, User, _Race } from '../../../../../../backend/Types/Types' import { _Rank, _Class, Class, User, _Race } from '../../../../../../backend/Types/Types'
import { specs } from '../../../../../../backend/Types/PlayerSpecs' import { specs } from '../../../../../../backend/Types/PlayerSpecs'
import { race } from 'rxjs';
import { hash, IApiService } from '../../services/ApiService'; import { hash, IApiService } from '../../services/ApiService';
@@ -33,7 +31,8 @@ export class RegisterComponent implements OnInit{
submitted = false submitted = false
constructor( constructor(
private router : Router, private router : Router,
private route : ActivatedRoute,
private api : IApiService private api : IApiService
){} ){}
@@ -84,6 +83,11 @@ export class RegisterComponent implements OnInit{
userid: this.api.getAuth().user.id!, userid: this.api.getAuth().user.id!,
race: char.race race: char.race
}) })
this.route.queryParamMap.subscribe(params => {
this.router.navigate([params.get('redirect')])
})
}catch(e){ }catch(e){
alert("Error creating character"+e) alert("Error creating character"+e)
return return
@@ -2,7 +2,6 @@ import { Component, OnInit } from '@angular/core';
import { Item, Stats } from '../../../../../../backend/Types/Types'; import { Item, Stats } from '../../../../../../backend/Types/Types';
import { NbToastrService } from '@nebular/theme'; import { NbToastrService } from '@nebular/theme';
import { ClientApiService } from '../../services/client-login-api';
import { IApiService } from '../../services/ApiService'; import { IApiService } from '../../services/ApiService';
@Component({ @Component({
@@ -1,6 +1,5 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, OnInit, Input } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { ClientApiService as ClientApiService } from '../../services/client-login-api';
import { Spec, User, Character, Item } from '../../../../../../backend/Types/Types'; import { Spec, User, Character, Item } from '../../../../../../backend/Types/Types';
import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
import { _Tiers } from '../../../../../../backend/Types/Items'; import { _Tiers } from '../../../../../../backend/Types/Items';
@@ -1,10 +1,8 @@
import { Component, AfterContentInit, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { NbMenuItem } from '@nebular/theme'; import { NbMenuItem } from '@nebular/theme';
import { ClientApiService } from '../services/client-login-api';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { IApiService } from '../services/ApiService'; import { IApiService } from '../services/ApiService';
declare const Cookies
@Component({ @Component({
selector: 'page-layout', selector: 'page-layout',
@@ -16,7 +14,7 @@ declare const Cookies
</ngx-one-column-layout> </ngx-one-column-layout>
`, `,
}) })
export class PagesLayoutComponent implements OnInit{ export class PagesLayoutComponent{
menu:NbMenuItem[] = [{ menu:NbMenuItem[] = [{
icon: 'image', icon: 'image',
@@ -36,18 +34,6 @@ export class PagesLayoutComponent implements OnInit{
link: '/frontcraft/armory' link: '/frontcraft/armory'
}] }]
constructor( constructor(){}
private api : IApiService,
private router : Router
){}
ngOnInit(): void {
/*
const u = this.api.getCurrentUser()
if(!u){
this.router.navigate(['/auth/login'])
}
*/
}
} }
@@ -9,7 +9,6 @@ import {
NbInputModule, NbInputModule,
NbMenuModule, NbMenuModule,
NbCardModule, NbCardModule,
NbTreeGridModule,
NbListModule, NbListModule,
NbTabsetModule, NbTabsetModule,
NbIconModule, NbIconModule,
@@ -1,6 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { ClientApiService as ClientApiService } from '../../services/client-login-api';
import { RaidData } from '../../../../../../backend/Types/Types'; import { RaidData } from '../../../../../../backend/Types/Types';
import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
import { IApiService } from '../../services/ApiService'; import { IApiService } from '../../services/ApiService';
@@ -34,7 +33,7 @@ export class FrontcraftArchiveComponent implements OnInit{
tanks:[], tanks:[],
healers: [] healers: []
} }
tokens = {} tokens: {[key in string]:any[]} = {}
displayedtokens = {} displayedtokens = {}
search = "" search = ""
@@ -53,6 +52,10 @@ export class FrontcraftArchiveComponent implements OnInit{
const raidManager = this.api.get('RaidManager') const raidManager = this.api.get('RaidManager')
const raiddata = await raidManager.getArchiveRaid(parseInt(param)) const raiddata = await raidManager.getArchiveRaid(parseInt(param))
console.log(raiddata);
this.raid = raiddata this.raid = raiddata
this.isTier = raiddata.tier != null this.isTier = raiddata.tier != null
this.tokens = raiddata.tokens this.tokens = raiddata.tokens
@@ -69,8 +72,18 @@ export class FrontcraftArchiveComponent implements OnInit{
this.changeSearch() this.changeSearch()
} }
changeSearch = () => { changeSearch(){
this.tokens = this.raid.tokens; if(!this.search || this.search == ""){
this.displayedtokens = this.raid.tokens; this.displayedtokens = this.tokens
}else{
this.displayedtokens = {}
Object.entries(this.tokens).forEach((e) => {
const filteredTokens = e[1].filter(item => {
return item.itemname.toLocaleLowerCase().includes(this.search.toLocaleLowerCase())
})
if(filteredTokens.length > 0)
this.displayedtokens[e[0]] = filteredTokens
})
}
} }
} }
@@ -3,34 +3,41 @@
Pick Character Pick Character
</nb-card-header> </nb-card-header>
<nb-card-body> <nb-card-body>
<div *ngFor="let character of characters"> <textarea
<img [src]="'../../../../assets/images/'+character.class.toLowerCase()+'.png'" /> placeholder="Add a note (optional)"
<span [ngStyle]="{'color': character.color}"> rows="7"
cols="50"
nbInput style="white-space: pre-line;"
[(ngModel)]="memo">
</textarea>
<br />
<br />
<nb-card *ngFor="let character of characters">
<nb-card-header [ngStyle]="{'color': character.color}">
<img [src]="'../../../../assets/images/'+character.class.toLowerCase()+'.png'" />
{{character.charactername}} {{character.charactername}}
</span> </nb-card-header>
<br > <nb-card-body>
<br > <button
<br > (click)="signup(character, false)"
nbButton
<button outline
(click)="signup(character, false)" status="success"
nbButton size="medium">
outline <nb-icon icon="checkmark-outline"></nb-icon> On Time
status="success" </button>
size="medium"> &nbsp;
<nb-icon icon="checkmark-outline"></nb-icon> On Time &nbsp;
</button> &nbsp;
&nbsp; <button
&nbsp; (click)="signup(character, true)"
&nbsp; nbButton
<button outline
(click)="signup(character, true)" status="warning"
nbButton size="medium">
outline <nb-icon icon="clock-outline"></nb-icon> Late
status="warning" </button>
size="medium"> </nb-card-body>
<nb-icon icon="clock-outline"></nb-icon> Late </nb-card>
</button>
</div>
</nb-card-body> </nb-card-body>
</nb-card> </nb-card>
@@ -1,7 +1,6 @@
import { OnInit, Component } from '@angular/core'; import { OnInit, Component } from '@angular/core';
import { NbWindowRef, NbToastrService, NbDialogRef } from '@nebular/theme'; import { NbToastrService, NbDialogRef } from '@nebular/theme';
import { RaidData, Character } from '../../../../../../backend/Types/Types'; import { RaidData, Character } from '../../../../../../backend/Types/Types';
import { ClientApiService } from '../../services/client-login-api';
import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
import { IApiService } from '../../services/ApiService'; import { IApiService } from '../../services/ApiService';
@@ -13,6 +12,7 @@ export class FrontcraftCharacerpickerComponent implements OnInit{
raid : RaidData raid : RaidData
characters: Character[] characters: Character[]
memo: string
constructor( constructor(
protected dialogRef: NbDialogRef<FrontcraftCharacerpickerComponent>, protected dialogRef: NbDialogRef<FrontcraftCharacerpickerComponent>,
@@ -25,7 +25,7 @@ export class FrontcraftCharacerpickerComponent implements OnInit{
const signup = this.api.get('signup') const signup = this.api.get('signup')
if(!signup) return if(!signup) return
await signup.sign(auth.token.value, character, this.raid, late) await signup.sign(auth.token.value, character, this.raid, late, this.memo)
this.toast.show('Signup', 'Success', { status: 'success' }) this.toast.show('Signup', 'Success', { status: 'success' })
this.dialogRef.close() this.dialogRef.close()
} }
@@ -5,34 +5,47 @@
<nb-tabset> <nb-tabset>
<nb-tab tabTitle="Info"> <nb-tab tabTitle="Info">
<h1> <h1>
<img [src]="'../../../../assets/images/'+(raid.tier || 'null').toLowerCase()+'.png'" <img [src]="'/assets/images/'+(raid.tier || 'null').toLowerCase()+'.png'"
style="height: 100px" /> style="height: 100px" />
{{raid.title}} {{raid.title}}
</h1> </h1>
<p> <p>
{{raid.signupcount}} / {{raid.size}} signups {{raid.signupcount}} / {{raid.size}} signups
</p> </p>
<p> <p style="white-space: pre-line;">
{{raid.description}} {{raid.description}}
</p> </p>
<div *ngIf="canSignup"> <div *ngIf="canSignup">
<div *ngIf="isSignedup"> <div *ngIf="isSignedup" style="width: 100%;">
<p style="white-space: pre-line;">
<p> You are signed as: <span
You are signed as: {{mySignup.charactername}} ({{mySignup.race}} style="text-transform: capitalize; word-break: keep-all;">{{mySignup.charactername}}</span>
{{mySignup.specname}} ({{mySignup.race}} {{mySignup.specname}} {{mySignup.class}})
{{mySignup.class}})<br /> <br />
Status: {{mySignup.status}} Status: {{mySignup.status}}<br /><br />
<textarea [status]="mySignup.status === 'Attending'?'success':'warning'"
placeholder="Add a note (optional)" rows="10" cols="50" nbInput
style="white-space: pre-line;" [(ngModel)]="mySignup.memo">
</textarea>
</p> </p>
<button (click)="setLate(true)" *ngIf="mySignup.status !== 'Late'" nbButton outline <button *ngIf="mySignup.status === 'Late'" (click)="setLate(false)" nbButton outline
status="warning" size="medium">
<nb-icon icon="clock-outline"></nb-icon> Late
</button>
<button (click)="setLate(false)" *ngIf="mySignup.status === 'Late'" nbButton outline
status="success" size="medium"> status="success" size="medium">
<nb-icon icon="checkmark-outline"></nb-icon> On Time <nb-icon icon="checkmark-outline"></nb-icon> On Time
</button> </button>
<button *ngIf="mySignup.status === 'Attending'" (click)="setLate(false)" nbButton
outline status="success" size="medium">
<nb-icon icon="checkmark-outline"></nb-icon> Update
</button>
<button *ngIf="mySignup.status === 'Late'" (click)="setLate(true)" nbButton outline
status="warning" size="medium">
<nb-icon icon="clock-outline"></nb-icon> Update
</button>
<button *ngIf="mySignup.status === 'Attending'" (click)="setLate(true)" nbButton outline
status="warning" size="medium">
<nb-icon icon="clock-outline"></nb-icon> Late
</button>
<button (click)="unsign()" nbButton outline status="danger" size="medium"> <button (click)="unsign()" nbButton outline status="danger" size="medium">
<nb-icon icon="close"></nb-icon>unsign <nb-icon icon="close"></nb-icon>unsign
</button> </button>
@@ -46,7 +59,6 @@
</nb-tab> </nb-tab>
<nb-tab tabTitle="Signups" [badgeText]="raid.signupcount" badgePosition="top right" <nb-tab tabTitle="Signups" [badgeText]="raid.signupcount" badgePosition="top right"
[badgeStatus]="raid.signupcount<40?'warning':'success'"> [badgeStatus]="raid.signupcount<40?'warning':'success'">
<div class="row"> <div class="row">
<nb-card class="col-12 col-md-6" *ngIf="raid.tanks.length > 0"> <nb-card class="col-12 col-md-6" *ngIf="raid.tanks.length > 0">
<nb-card-header>Tanks ({{raid.tanks.length}})</nb-card-header> <nb-card-header>Tanks ({{raid.tanks.length}})</nb-card-header>
@@ -63,11 +75,28 @@
<a [ngStyle]="{'color': participant.color}" style="text-transform: capitalize;" <a [ngStyle]="{'color': participant.color}" style="text-transform: capitalize;"
[routerLink]="'/frontcraft/character/'+participant.charactername"> [routerLink]="'/frontcraft/character/'+participant.charactername">
<img style="width:20px; height:20px" <img style="width:20px; height:20px"
[src]="'../../../../assets/images/'+participant.race.toLowerCase()+'_xs.gif'" /> [src]="'/assets/images/'+participant.race.toLowerCase()+'_xs.gif'" />
<img style="width:20px; height:20px" <img style="width:20px; height:20px"
[src]="'../../../../assets/images/'+participant.class.toLowerCase()+'.png'" /> [src]="'/assets/images/'+participant.class.toLowerCase()+'.png'" />
{{ participant.charactername }} {{ participant.charactername }}
</a> </a>
<ng-template #template>
<div style="padding: 10px">
<p
style="white-space: pre-line; max-width: 50vw; word-wrap: break-word;">
{{participant.memo}}
</p>
{{participant.timestamp | date : 'HH:mm EEE MMM d'}}
</div>
</ng-template>
<nb-icon *ngIf="participant.memo == null || participant.memo == ''"
[nbPopover]="template" nbPopoverTrigger="hover"
style="width: 0.75em; height: 0.75em;" icon="clock-outline"></nb-icon>
<nb-icon *ngIf="participant.memo != null && participant.memo != ''"
[nbPopover]="template" nbPopoverTrigger="hover"
style="width: 0.75em; height: 0.75em;" icon="message-circle-outline" status="info">
</nb-icon>
<span *ngIf="participant.rank=='Trial'" style="font-size: 9px;"> <span *ngIf="participant.rank=='Trial'" style="font-size: 9px;">
Trial Trial
</span> </span>
@@ -90,11 +119,27 @@
<a [ngStyle]="{'color': participant.color}" style="text-transform: capitalize;" <a [ngStyle]="{'color': participant.color}" style="text-transform: capitalize;"
[routerLink]="'/frontcraft/character/'+participant.charactername"> [routerLink]="'/frontcraft/character/'+participant.charactername">
<img style="width:20px; height:20px" <img style="width:20px; height:20px"
[src]="'../../../../assets/images/'+participant.race.toLowerCase()+'_xs.gif'" /> [src]="'/assets/images/'+participant.race.toLowerCase()+'_xs.gif'" />
<img style="width:20px; height:20px" <img style="width:20px; height:20px"
[src]="'../../../../assets/images/'+participant.class.toLowerCase()+'.png'" /> [src]="'/assets/images/'+participant.class.toLowerCase()+'.png'" />
{{ participant.charactername }} {{ participant.charactername }}
</a> </a>
<ng-template #template>
<div style="padding: 10px">
<p style="white-space: pre-line;">
{{participant.memo}}
</p>
{{participant.timestamp | date : 'HH:mm EEE MMM d'}}
</div>
</ng-template>
<nb-icon *ngIf="participant.memo == null || participant.memo == ''"
[nbPopover]="template" nbPopoverTrigger="hover"
style="width: 0.75em; height: 0.75em;" icon="clock-outline"></nb-icon>
<nb-icon *ngIf="participant.memo != null && participant.memo != ''"
[nbPopover]="template" nbPopoverTrigger="hover"
style="width: 0.75em; height: 0.75em;" icon="message-circle-outline" status="info">
</nb-icon>
<span *ngIf="participant.rank=='Trial'" style="font-size: 9px;"> <span *ngIf="participant.rank=='Trial'" style="font-size: 9px;">
Trial Trial
</span> </span>
@@ -119,11 +164,27 @@
style="text-transform: capitalize;" style="text-transform: capitalize;"
[routerLink]="'/frontcraft/character/'+participant.charactername"> [routerLink]="'/frontcraft/character/'+participant.charactername">
<img style="width:20px; height:20px" <img style="width:20px; height:20px"
[src]="'../../../../assets/images/'+participant.race.toLowerCase()+'_xs.gif'" /> [src]="'/assets/images/'+participant.race.toLowerCase()+'_xs.gif'" />
<img style="width:20px; height:20px" <img style="width:20px; height:20px"
[src]="'../../../../assets/images/'+participant.class.toLowerCase()+'.png'" /> [src]="'/assets/images/'+participant.class.toLowerCase()+'.png'" />
{{ participant.charactername }} {{ participant.charactername }}
</a> </a>
<ng-template #template>
<div style="padding: 10px">
<p style="white-space: pre-line;">
{{participant.memo}}
</p>
{{participant.timestamp | date : 'HH:mm EEE MMM d'}}
</div>
</ng-template>
<nb-icon *ngIf="participant.memo == null || participant.memo == ''"
[nbPopover]="template" nbPopoverTrigger="hover"
style="width: 0.75em; height: 0.75em;" icon="clock-outline"></nb-icon>
<nb-icon *ngIf="participant.memo != null && participant.memo != ''"
[nbPopover]="template" nbPopoverTrigger="hover"
style="width: 0.75em; height: 0.75em;" icon="message-circle-outline" status="info">
</nb-icon>
<span *ngIf="participant.rank=='Trial'" style="font-size: 9px;"> <span *ngIf="participant.rank=='Trial'" style="font-size: 9px;">
Trial Trial
</span> </span>
@@ -147,7 +208,8 @@
<nb-card-body> <nb-card-body>
<nb-tabset> <nb-tabset>
<nb-tab tabTitle="Reserves" [active]="reservesShown"> <nb-tab tabTitle="Reserves" [active]="reservesShown">
<input type="text" nbInput [(ngModel)]="search" (change)="changeSearch()" placeholder="Search" fullWidth="true"> <input type="text" nbInput [(ngModel)]="search" (change)="changeSearch()" placeholder="Search"
fullWidth="true">
<nb-list class=""> <nb-list class="">
<nb-list-item *ngFor="let item of displayedtokens | keyvalue"> <nb-list-item *ngFor="let item of displayedtokens | keyvalue">
@@ -161,15 +223,13 @@
<tr> <tr>
<td> <td>
<div class="row"> <div class="row">
<div <div *ngFor="let token of item.value" class="col-12 col-md-6 col-xl-4">
*ngFor="let token of item.value"
class="col-12 col-md-6 col-xl-4">
<span <span
[ngStyle]="{'text-decoration': token.rank=='Trial'? 'line-through' : 'none currentcolor solid' }"> [ngStyle]="{'text-decoration': token.rank=='Trial'? 'line-through' : 'none currentcolor solid' }">
<span style="text-transform: capitalize;"> <span style="text-transform: capitalize; white-space: nowrap;">
[&nbsp;{{token.level}}&nbsp;]&nbsp; [ {{token.level}} ]&nbsp;
<a [routerLink]="'/frontcraft/character/'+token.charactername" <a [routerLink]="'/frontcraft/character/'+token.charactername"
[ngStyle]="{'color':token.level>=10?'#ff8000':token.level>=8?'#a335ee':token.level>=6?'#0070dd':token.level>=4?'#1eff00':token.level>=2?'#ffffff':'#9d9d9d'}"> [ngStyle]="{'color':token.level>=10?'#ff8000':token.level>=8?'#a335ee':token.level>=6?'#0070dd':token.level>=4?'#1eff00':token.level>=2?'#ffffff':'#9d9d9d'}">
{{token.charactername}} {{token.charactername}}
</a> </a>
</span> </span>
@@ -179,13 +239,14 @@
Becomes valid when promoted to Raider Becomes valid when promoted to Raider
</div> </div>
</ng-template> </ng-template>
<span *ngIf="token.rank=='Trial'" style="font-size: 9px;" [nbPopover]="tooltip" <span *ngIf="token.rank=='Trial'" style="font-size: 9px;"
nbPopoverTrigger="hover" nbPopoverPlacement="top"> [nbPopover]="tooltip" nbPopoverTrigger="hover"
nbPopoverPlacement="top">
Trial Trial
</span><br /> </span><br />
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
</table> </table>
@@ -1,19 +1,22 @@
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router, NavigationStart } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { ClientApiService as ClientApiService } from '../../services/client-login-api';
import { RaidData, Raid, Signup, Character, Spec, Item, SRToken } from '../../../../../../backend/Types/Types'; import { RaidData, Raid, Signup, Character, Spec, Item, SRToken } from '../../../../../../backend/Types/Types';
import { NbWindowService, NbToastrService, NbDialogService } from '@nebular/theme'; import { NbToastrService, NbDialogService } from '@nebular/theme';
import { FrontcraftCharacerpickerComponent } from './characterpicker.component'; import { FrontcraftCharacerpickerComponent } from './characterpicker.component';
import { getClassColor, SpecT } from '../../../../../../backend/Types/PlayerSpecs'; import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
import { FrontcraftBuyTokenComponent } from '../shop/buytoken.component'; import { FrontcraftBuyTokenComponent } from '../shop/buytoken.component';
import { allItems } from '../../../../../../backend/Types/Items'; import { allItems } from '../../../../../../backend/Types/Items';
import { IApiService } from '../../services/ApiService'; import { IApiService } from '../../services/ApiService';
import { UpdatingComponent } from '../../UpdatingComponent';
import { LoggerService } from '../../services/logger.service';
@Component({ @Component({
selector: 'raid', selector: 'raid',
templateUrl: './raid.component.html', templateUrl: './raid.component.html',
}) })
export class FrontcraftRaidComponent implements OnInit, OnDestroy{ export class FrontcraftRaidComponent
extends UpdatingComponent
implements OnInit{
canSignup = false canSignup = false
isSignedup = false isSignedup = false
@@ -43,41 +46,28 @@ export class FrontcraftRaidComponent implements OnInit, OnDestroy{
tokens: {[itemname in string]: (Character & SRToken & Item)[]} = {} tokens: {[itemname in string]: (Character & SRToken & Item)[]} = {}
displayedtokens = {} displayedtokens = {}
search = "" search = ""
uuid
constructor( constructor(
private api: IApiService, private api: IApiService,
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
private dialogService : NbDialogService, private dialogService : NbDialogService,
private toast: NbToastrService private toast: NbToastrService,
logger: LoggerService
){ ){
router.events.subscribe(event => { super(router, api, logger)
if (event instanceof NavigationStart) {
this.ngOnDestroy()
}
})
} }
ngOnDestroy = () => { ngOnInit(){
if(this.uuid)
this.api.get('PubSub').unsubscribe(this.uuid)
}
async ngOnInit(){
this.manageRaid = this.api.get('manageRaid') this.manageRaid = this.api.get('manageRaid')
const signupFeature = this.api.get('signup') const signupFeature = this.api.get('signup')
if(signupFeature){ if(signupFeature){
this.canSignup = true this.canSignup = true
} }
await this.refresh() this.refresh().then(() => {
this.subscribe(String(this.raid.id), (data: RaidData) => this.display(data))
const res = await this.api.get('PubSub').subscribe(""+this.raid.id!, (data: RaidData) => {
this.display(data)
}) })
this.uuid = res.uuid
} }
itemSelect = async(item) => { itemSelect = async(item) => {
@@ -143,7 +133,7 @@ export class FrontcraftRaidComponent implements OnInit, OnDestroy{
await signup.sign(auth.token.value, { await signup.sign(auth.token.value, {
...this.mySignup, ...this.mySignup,
id: this.mySignup.characterid, id: this.mySignup.characterid,
}, this.raid, value) }, this.raid, value, this.mySignup.memo)
this.toast.show('Signup', 'Success', { status: 'success' }) this.toast.show('Signup', 'Success', { status: 'success' })
} }
@@ -163,9 +153,7 @@ export class FrontcraftRaidComponent implements OnInit, OnDestroy{
const raiddata = await raidManager.getRaidData(<any>{ const raiddata = await raidManager.getRaidData(<any>{
id: param id: param
}) })
this.display(raiddata) this.display(raiddata)
} }
display = async (raiddata:RaidData) => { display = async (raiddata:RaidData) => {
@@ -179,15 +167,17 @@ export class FrontcraftRaidComponent implements OnInit, OnDestroy{
}) })
const user = this.api.getCurrentUser() const user = this.api.getCurrentUser()
const matchingSignup = Object.values(raiddata.participants).flat().find(char => user && char.userid === user.id!) const matchingSignup = Object.values(raiddata.participants).flat().find(char => user && char.userid === user.id!)
if(matchingSignup){ if(matchingSignup){
this.isSignedup = true
this.mySignup = matchingSignup this.mySignup = matchingSignup
this.isSignedup = true
this.mySignup['status'] = matchingSignup['benched']?'Bench':matchingSignup['late']?'Late':'Attending' this.mySignup['status'] = matchingSignup['benched']?'Bench':matchingSignup['late']?'Late':'Attending'
}else{ }else{
this.isSignedup = false this.isSignedup = false
this.mySignup = null this.mySignup = null
} }
this.changeSearch() this.changeSearch()
} }
@@ -202,9 +192,7 @@ export class FrontcraftRaidComponent implements OnInit, OnDestroy{
}) })
if(filteredTokens.length > 0) if(filteredTokens.length > 0)
this.displayedtokens[e[0]] = filteredTokens this.displayedtokens[e[0]] = filteredTokens
}) })
console.log(this.displayedtokens);
} }
} }
@@ -1,7 +1,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ClientApiService } from '../../services/client-login-api';
import { Raid, RaidData } from '../../../../../../backend/Types/Types'; import { Raid, RaidData } from '../../../../../../backend/Types/Types';
import { NbWindowRef, NbDialogRef } from '@nebular/theme'; import { NbDialogRef } from '@nebular/theme';
import { Tiers, _Tiers } from '../../../../../../backend/Types/Items'; import { Tiers, _Tiers } from '../../../../../../backend/Types/Items';
import { IApiService } from '../../services/ApiService'; import { IApiService } from '../../services/ApiService';
@@ -31,7 +30,7 @@ export class FrontcraftCreateRaidsComponent implements OnInit {
private api: IApiService private api: IApiService
) {} ) {}
loadNext(cardData) { loadNext() {
} }
onTierSelect(){ onTierSelect(){
@@ -1,64 +1,70 @@
<nb-card class="col-12 col-xl-9"> <nb-card class="col-12 col-xl-9">
<nb-card-header> <nb-card-header>
Upcoming raids Upcoming raids
<nb-icon <nb-icon style="color:orange" *ngIf="manageRaid" (click)="create()" icon="plus-square-outline"></nb-icon>
style="color:orange"
*ngIf="manageRaid"
(click)="create()"
icon="plus-square-outline"></nb-icon>
</nb-card-header> </nb-card-header>
<nb-list <nb-list nbInfiniteList listenWindowScroll [threshold]="500">
nbInfiniteList <nb-list-item *ngFor="let raid of raids" class="raidlist">
listenWindowScroll <a [routerLink]="'/frontcraft/raid/'+raid.id">
[threshold]="500"> <div class="row">
<nb-list-item *ngFor="let raid of raids" <table>
[routerLink]="'/frontcraft/raid/'+raid.id" <tr>
class="raidlist" <td rowspan="2" style="padding-right: 20px;">
style="cursor: pointer;"> <img [src]="'/assets/images/'+(raid.tier || 'null').toLowerCase()+'.png'"
<div class="row"> style="max-width: 50px; object-fit: contain" />
<img [src]="'../../../../assets/images/'+(raid.tier || 'null').toLowerCase()+'.png'" </td>
style="object-fit: contain"
class="col-1 col-s-3" />
<div class="col-11 col-s-9" >
<div class="row">
<div class="col-12" style="padding-top: 5px">
<h4> <h4>
{{raid.title}} {{raid.title}}
</h4> </h4>
</div> </tr>
</div> <tr>
<div class="row" style="padding-top: 5px; padding-bottom: 5px; color:darkgray"> <td style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
<div class="col-6 col-md-3"> <nb-icon icon="checkmark-circle"></nb-icon>&nbsp;{{raid.signupcount}}&nbsp;/&nbsp;{{raid.size}}<br>
<nb-icon icon="checkmark-circle"></nb-icon> {{raid.signupcount}} / {{raid.size}}<br> </td>
</div> <td style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
<nb-icon icon="clock-outline"></nb-icon>&nbsp;{{raid.start | date : 'HH:mm'}}
<div class="col-6 col-md-3"> </td>
<nb-icon icon="clock-outline"></nb-icon> {{raid.start | date : 'HH:mm'}} <td style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
</div> <nb-icon icon="calendar-outline"></nb-icon>&nbsp;{{raid.start | date : 'EEE MMM d'}}
</td>
<div class="col-12 col-md-6"> </tr>
<nb-icon icon="calendar-outline"></nb-icon> {{raid.start | date : 'EEE MMM d'}} </table>
</div>
</div>
</div> </div>
</div> </a>
</nb-list-item> </nb-list-item>
</nb-list> </nb-list>
</nb-card> </nb-card>
<nb-card class="col-12 col-xl-9"> <nb-card class="col-12 col-xl-9">
<nb-card-header>Previous raids</nb-card-header> <nb-card-header>Previous raids</nb-card-header>
<nb-list <nb-list nbInfiniteList listenWindowScroll [threshold]="500">
nbInfiniteList
listenWindowScroll
[threshold]="500">
<nb-list-item *ngFor="let raid of oldraids" [routerLink]="'/frontcraft/archive/'+raid.id"> <nb-list-item *ngFor="let raid of oldraids" [routerLink]="'/frontcraft/archive/'+raid.id">
<b>{{raid.title}}</b><br> <a [routerLink]="'/frontcraft/archive/'+raid.id">
{{raid.signupcount}} / {{raid.size}}<br> <div class="row">
{{raid.start | date : 'EEEE d MMMM @ HH:mm'}}<br> <table>
{{raid.description}}<br> <tr>
<td rowspan="2" style="padding-right: 20px;">
<img [src]="'/assets/images/'+(raid.tier || 'null').toLowerCase()+'.png'"
style="max-width: 50px;;object-fit: contain; filter: grayscale(75%)" />
</td>
<h4>
{{raid.title}}
</h4>
</tr>
<tr>
<td style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
<nb-icon icon="checkmark-circle"></nb-icon>&nbsp;{{raid.signupcount}}&nbsp;/&nbsp;{{raid.size}}<br>
</td>
<td style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
<nb-icon icon="clock-outline"></nb-icon>&nbsp;{{raid.start | date : 'HH:mm'}}
</td>
<td style="padding-right: 25px; white-space: nowrap; color: lightslategray;">
<nb-icon icon="calendar-outline"></nb-icon>&nbsp;{{raid.start | date : 'EEE MMM d'}}
</td>
</tr>
</table>
</div>
</a>
</nb-list-item> </nb-list-item>
</nb-list> </nb-list>
</nb-card> </nb-card>
@@ -1,69 +1,59 @@
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Component, OnInit, OnDestroy } from '@angular/core';
import { ClientApiService } from '../../services/client-login-api'; import { NbDialogService } from '@nebular/theme';
import { NbWindowService, NbDialogService } from '@nebular/theme';
import { FrontcraftCreateRaidsComponent } from './createraid.compontent'; import { FrontcraftCreateRaidsComponent } from './createraid.compontent';
import { Router, NavigationStart } from '@angular/router'; import { Router, NavigationStart } from '@angular/router';
import { IApiService } from '../../services/ApiService'; import { IApiService } from '../../services/ApiService';
import { UpdatingComponent } from '../../UpdatingComponent';
import { LoggerService } from '../../services/logger.service';
@Component({ @Component({
selector: 'raids', selector: 'raids',
templateUrl: 'raids.component.html', templateUrl: 'raids.component.html',
styleUrls: ['raids.component.scss'], styleUrls: ['raids.component.scss'],
}) })
export class FrontcraftRaidsComponent implements OnInit, OnDestroy{ export class FrontcraftRaidsComponent
extends UpdatingComponent
implements OnInit {
manageRaid manageRaid
raids = [] raids = []
oldraids = [] oldraids = []
pageSize = 10; pageSize = 10;
uuid
constructor( constructor(
private api: IApiService, private api: IApiService,
private router: Router, router: Router,
private dialogService: NbDialogService private dialogService: NbDialogService,
logger: LoggerService
) { ) {
this.manageRaid = this.api.get('manageRaid') super(router, api, logger)
router.events.subscribe(event => {
if (event instanceof NavigationStart) {
this.ngOnDestroy()
}
})
} }
ngOnDestroy = () => { ngOnDestroy(){
if(this.uuid) super.ngOnDestroy()
this.api.get('PubSub').unsubscribe(this.uuid)
} }
async ngOnInit() { async ngOnInit() {
const res = await this.api.get('PubSub').subscribe("raids", () => { this.subscribe("raids", () => this.refresh())
this.refresh()
}) this.manageRaid = this.api.get('manageRaid')
this.uuid = res.uuid
this.refresh() this.refresh()
} }
refresh() { async refresh() {
this.api.get('RaidManager').getRaids().then(raids => { this.raids = await this.api.get('RaidManager').getRaids()
this.raids = raids
})
const raidManager = this.api.get('RaidManager') const raidManager = this.api.get('RaidManager')
raidManager.getPastRaids(10).then(raiddata => { this.oldraids = await raidManager.getPastRaids(10)
this.oldraids = raiddata
})
} }
create(){ create() {
this.dialogService.open(FrontcraftCreateRaidsComponent, { this.dialogService.open(FrontcraftCreateRaidsComponent, {
closeOnBackdropClick: true, closeOnBackdropClick: true,
closeOnEsc: true, closeOnEsc: true,
context: { context: {
templates: this.oldraids templates: this.oldraids
} }
}) })
} }
} }
@@ -1,10 +1,8 @@
import { Component, OnInit, ContentChild, AfterContentInit, ViewChild, AfterViewInit, Input, Output, EventEmitter } from '@angular/core'; import { Component, OnInit, Input } from '@angular/core';
import { ClientApiService as ClientApiService } from '../../services/client-login-api'; import { NbToastrService, NbDialogRef } from '@nebular/theme';
import { FrontcraftItemSelectComponent } from './itemselector.component';
import { NbWindowService, NbWindowRef, NbToastrService, NbDialogService, NbDialogRef } from '@nebular/theme';
import { Item, Character, SRToken, Signup } from '../../../../../../backend/Types/Types'; import { Item, Character, SRToken, Signup } from '../../../../../../backend/Types/Types';
import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
import { _Tiers, allItems, Tiers } from '../../../../../../backend/Types/Items'; import { _Tiers, Tiers } from '../../../../../../backend/Types/Items';
import { IApiService } from '../../services/ApiService'; import { IApiService } from '../../services/ApiService';
@Component({ @Component({
@@ -24,8 +24,16 @@ export class FrontcraftItemSelectComponent implements OnInit{
async ngOnInit(){ async ngOnInit(){
Promise.all(this.items.map(itemname =>
this.api.get('ItemManager').getItem(itemname)
Promise.all(this.items.map(async itemname => {
try{
return await this.api.get('ItemManager').getItem(itemname)
}catch(e){
console.log(itemname);
}
}
)).then(items => { )).then(items => {
this.allItems = items this.allItems = items
this.displayedItems = this.allItems this.displayedItems = this.allItems
@@ -1,6 +1,4 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { ClientApiService as ClientApiService } from '../../services/client-login-api';
import { NbDialogService } from '@nebular/theme';
import { Item, Character, Signup } from '../../../../../../backend/Types/Types'; import { Item, Character, Signup } from '../../../../../../backend/Types/Types';
import { _Tiers, allItems } from '../../../../../../backend/Types/Items'; import { _Tiers, allItems } from '../../../../../../backend/Types/Items';
@@ -57,7 +57,7 @@ accent="info">
</form> </form>
<br /> <br />
<h3>Avaiable Reserves</h3> <h3>Available Reserves</h3>
{{user.MC}} MC | {{user.MC}} MC |
{{user.BWL}} BWL | {{user.BWL}} BWL |
{{user.ZG}} ZG | {{user.ZG}} ZG |
@@ -1,6 +1,4 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ClientApiService as ClientApiService } from '../../services/client-login-api';
import { Router } from '@angular/router';
import { _Rank, _Class, _Race, RPCPermission } from '../../../../../../backend/Types/Types' import { _Rank, _Class, _Race, RPCPermission } from '../../../../../../backend/Types/Types'
import { NbToastrService } from '@nebular/theme'; import { NbToastrService } from '@nebular/theme';
import { IApiService } from '../../services/ApiService'; import { IApiService } from '../../services/ApiService';
@@ -1,5 +1,4 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ClientApiService } from '../services/client-login-api';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
@Component({ @Component({
@@ -1,23 +1,30 @@
import { Auth, FrontcraftIfc, FrontcraftFeatureIfc, User, SomeOf } from '../../../../../backend/Types/Types'; import { Auth, FrontcraftIfc, FrontcraftFeatureIfc, User, SomeOf } from '../../../../../backend/Types/Types';
import { saltedHash } from '../../../../../backend/Util/hash'; import { saltedHash } from '../../../../../backend/Util/hash';
import { RPCSocket } from 'rpclibrary'; import { RPCSocket, ConnectedSocket } from 'rpclibrary';
export class IApiService{ export class IApiService{
getUnprivilegedSocket: () => RPCSocket & FrontcraftIfc getUnprivilegedSocket: () => ConnectedSocket<FrontcraftIfc>
getAuth: () => Auth | undefined getAuth: () => Auth | undefined
checkLogin: () => Promise<boolean> checkLogin: () => Promise<boolean>
initialize: () => Promise<any> initialize: () => Promise<any>
getCurrentUser: () => User | undefined getCurrentUser: () => User
login: (username: string, password: string) => Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>> login: (username: string, password: string) => Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>>
connectShoutbox: (callback:Function) => Promise<Function> connectShoutbox: (callback:Function) => Promise<Function>
kick: () => Promise<void> kick: () => Promise<void>
logout: () => Promise<void> logout: () => Promise<void>
get: <K extends (keyof FrontcraftIfc | keyof FrontcraftFeatureIfc)>(feature : K) => K extends keyof FrontcraftIfc?FrontcraftIfc[K]: get: <K extends (keyof FrontcraftIfc | keyof FrontcraftFeatureIfc)>(feature : K) => K extends keyof FrontcraftIfc
K extends keyof FrontcraftFeatureIfc?(FrontcraftFeatureIfc[K] | void): ? FrontcraftIfc[K]
never : K extends keyof FrontcraftFeatureIfc
? (FrontcraftFeatureIfc[K] | void)
: never
} }
export async function hash(value:string) : Promise<string>{ export async function hash(value:string) : Promise<string>{
return saltedHash(value, "") return saltedHash(value, "")
} }
export const GuestUser: User = {
username: 'Guest',
pwhash: '',
rank: 'Guest'
}
@@ -1,24 +1,28 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import {RPCSocket} from 'rpclibrary/js/src/Frontend' import { RPCSocket } from 'rpclibrary/js/src/Frontend'
import { ConnectedSocket } from "rpclibrary"
import { Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../backend/Types/Types' import { Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../backend/Types/Types'
import { ShoutMessage } from '../../../../../backend/Components/Shoutbox/Interface'; import { ShoutMessage } from '../../../../../backend/Components/Shoutbox/Interface';
import { hash, IApiService } from './ApiService'; import { hash, IApiService, GuestUser } from './ApiService';
import { LoggerService } from './logger.service';
declare const Cookies declare const Cookies
@Injectable() @Injectable()
export class ClientApiService implements IApiService{ export class ClientApiService implements IApiService{
private socket:RPCSocket & FrontcraftIfc; private socket:ConnectedSocket<FrontcraftIfc>
private auth:Auth private auth:Auth
private privSocket: RPCSocket & SomeOf<FrontcraftFeatureIfc> private privSocket: RPCSocket<SomeOf<FrontcraftFeatureIfc>>
constructor(){ constructor(
private logger: LoggerService
){
window['s'] = this window['s'] = this
} }
getUnprivilegedSocket = () : RPCSocket & FrontcraftIfc => this.socket getUnprivilegedSocket = () : ConnectedSocket<FrontcraftIfc> => this.socket
private getPrivilegedSocket = async (auth:Auth) : Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>> => { private getPrivilegedSocket = async (auth:Auth) : Promise<RPCSocket<SomeOf<FrontcraftFeatureIfc>>> => {
if(this.privSocket) { if(this.privSocket) {
return this.privSocket return this.privSocket
} }
@@ -38,17 +42,17 @@ export class ClientApiService implements IApiService{
sock.hook('getUserData', () => auth) sock.hook('getUserData', () => auth)
sock.on('close', async () => { sock.on('close', async () => {
console.log("priv#close"); this.logger.log("priv#close");
//handled via unprivileged socket //handled via unprivileged socket
}) })
sock.on('error', async (e) => { sock.on('error', async (e) => {
console.log("priv#error", e); this.logger.log("priv#error", e);
//handled via unprivileged socket //handled via unprivileged socket
}) })
const authSock = await sock.connect<RPCSocket & SomeOf<FrontcraftFeatureIfc>>(auth.token.value) const authSock = await sock.connect(auth.token.value)
this.auth = auth this.auth = auth
this.privSocket = authSock this.privSocket = authSock
@@ -74,8 +78,8 @@ export class ClientApiService implements IApiService{
} }
} }
getCurrentUser = () : User | undefined => { getCurrentUser = () : User => {
return this.auth?this.auth.user:undefined return this.auth?this.auth.user:GuestUser
} }
login = async (username: string, password: string) : Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>> => { login = async (username: string, password: string) : Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>> => {
@@ -102,8 +106,8 @@ export class ClientApiService implements IApiService{
} }
async connectShoutbox(callback:Function) : Promise<Function>{ async connectShoutbox(callback:Function) : Promise<Function>{
const res = await this.get('Shoutbox').subscribe(callback) const uuid = await this.get('Shoutbox').subscribe(callback)
return async (msg: ShoutMessage) => await this.get('Shoutbox').shout(res.uuid, msg) return async (msg: ShoutMessage) => await this.get('Shoutbox').shout(uuid, msg)
} }
kick = async () => { kick = async () => {
@@ -113,6 +117,7 @@ export class ClientApiService implements IApiService{
logout = async () => { logout = async () => {
Cookies.remove('token') Cookies.remove('token')
if(this.privSocket) this.privSocket.unhook('kick')
if(this.auth){ if(this.auth){
try{ try{
await this.socket.UserManager.logout(this.auth.user.username, this.auth.token.value) await this.socket.UserManager.logout(this.auth.user.username, this.auth.token.value)
@@ -133,11 +138,11 @@ export class ClientApiService implements IApiService{
} }
try{ try{
let conn = new RPCSocket(20000, window.location.hostname) let conn = new RPCSocket<FrontcraftIfc>(20000, window.location.hostname)
conn.on('close', async () => { conn.on('close', async () => {
}) })
this.socket = await conn.connect<FrontcraftIfc>() this.socket = await conn.connect()
}catch(e){ }catch(e){
alert('Unable to connect. The server appears to be down.\nPress OK to refresh the page') alert('Unable to connect. The server appears to be down.\nPress OK to refresh the page')
location.reload() location.reload()
@@ -0,0 +1,37 @@
import { Injectable } from "@angular/core";
import { environment } from '../../../environments/environment';
interface ILoggerService{
log,
warn,
error,
table,
collapsed
}
@Injectable()
export class LoggerService implements ILoggerService {
constructor(
private logger = console
) { }
log = (...args) => { this.LOG('log', args) }
warn = (...args) => { this.LOG('warn', args) }
error = (...args) => { this.LOG('error', args) }
table = (...args) => { this.LOG('table', args) }
collapsed = (groupname: string, type: keyof ILoggerService, ...args) => {
if(type === "collapsed")
return this.log(args)
console.groupCollapsed(groupname)
this[type as any].apply(this.logger, args)
console.groupEnd()
}
private LOG(type, ...args) {
if (!environment.production) {
this.logger[type].apply(this.logger, ...args)
}
}
}
@@ -1,19 +1,23 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import {RPCSocket} from 'rpclibrary/js/src/Frontend' import {RPCSocket} from 'rpclibrary/js/src/Frontend'
import { Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../backend/Types/Types' import { Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../backend/Types/Types'
import { hash, IApiService } from './ApiService'; import { hash, IApiService, GuestUser } from './ApiService';
import { LoggerService } from './logger.service';
import { ConnectedSocket } from 'rpclibrary';
declare const Cookies declare const Cookies
@Injectable() @Injectable()
export class ServerApiService implements IApiService{ export class ServerApiService implements IApiService{
private socket:RPCSocket & FrontcraftIfc; private socket:ConnectedSocket<FrontcraftIfc>
private auth:Auth private auth:Auth
private privSocket: RPCSocket & SomeOf<FrontcraftFeatureIfc> private privSocket: RPCSocket & SomeOf<FrontcraftFeatureIfc>
constructor(){} constructor(
private logger: LoggerService
){}
getUnprivilegedSocket = () : RPCSocket & FrontcraftIfc => this.socket getUnprivilegedSocket = () : ConnectedSocket<FrontcraftIfc> => this.socket
get = <K extends (keyof FrontcraftIfc | keyof FrontcraftFeatureIfc)>(feature : K) : K extends keyof FrontcraftIfc?FrontcraftIfc[K]: get = <K extends (keyof FrontcraftIfc | keyof FrontcraftFeatureIfc)>(feature : K) : K extends keyof FrontcraftIfc?FrontcraftIfc[K]:
K extends keyof FrontcraftFeatureIfc?(FrontcraftFeatureIfc[K] | void): K extends keyof FrontcraftFeatureIfc?(FrontcraftFeatureIfc[K] | void):
@@ -22,8 +26,8 @@ export class ServerApiService implements IApiService{
return this.socket[feature] return this.socket[feature]
} }
getCurrentUser = () : User | undefined => { getCurrentUser = () : User => {
return this.auth?this.auth.user:undefined return GuestUser
} }
login = async (username: string, password: string) : Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>> => { login = async (username: string, password: string) : Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>> => {
@@ -42,13 +46,13 @@ export class ServerApiService implements IApiService{
//attach error handler now so failed logins dont trigger reloads //attach error handler now so failed logins dont trigger reloads
this.socket.on('error', async (err) => { this.socket.on('error', async (err) => {
console.log(err); this.logger.log(err);
}) })
} }
async connectShoutbox(callback:Function) : Promise<Function>{ async connectShoutbox(callback:Function) : Promise<Function>{
return console.log return this.logger.log
} }
kick = async () => { kick = async () => {
@@ -60,7 +64,7 @@ export class ServerApiService implements IApiService{
try{ try{
await this.socket.UserManager.logout(this.auth.user.username, this.auth.token.value) await this.socket.UserManager.logout(this.auth.user.username, this.auth.token.value)
}catch(e){ }catch(e){
console.warn(e); this.logger.warn(e);
} }
} }
@@ -76,13 +80,13 @@ export class ServerApiService implements IApiService{
} }
try{ try{
let conn = new RPCSocket(20000, window.location.hostname) let conn = new RPCSocket<FrontcraftIfc>(20000, window.location.hostname)
conn.on('close', async () => { conn.on('close', async () => {
}) })
this.socket = await conn.connect<FrontcraftIfc>() this.socket = await conn.connect()
}catch(e){ }catch(e){
console.log(e); this.logger.log(e);
throw new Error("Websocket cannot connect") throw new Error("Websocket cannot connect")
} }
} }
+1 -1
View File
@@ -6,4 +6,4 @@ export { AppServerModule } from './app/app.server.module';
import { IApiService } from './app/frontcraft/services/ApiService'; import { IApiService } from './app/frontcraft/services/ApiService';
export { IApiService } export { IApiService }
export { ServerApiService } from './app/frontcraft/services/server-login-api'; export { ServerApiService } from './app/frontcraft/services/server-login-api';
export { LoggerService } from './app/frontcraft/services/logger.service'
+7 -2
View File
@@ -10,18 +10,23 @@ import { FrontcraftAppModule } from './app/app.module';
import { environment } from './environments/environment'; import { environment } from './environments/environment';
import { IApiService } from "./app/frontcraft/services/ApiService" import { IApiService } from "./app/frontcraft/services/ApiService"
import { ClientApiService } from './app/frontcraft/services/client-login-api'; import { ClientApiService } from './app/frontcraft/services/client-login-api';
import { LoggerService } from './app/frontcraft/services/logger.service';
if (environment.production) { if (environment.production) {
enableProdMode(); enableProdMode();
} }
const loggerService = new LoggerService()
const serviceObj = new ClientApiService() const serviceObj = new ClientApiService(loggerService)
serviceObj.initialize().then(_ => { serviceObj.initialize().then(_ => {
platformBrowserDynamic([ platformBrowserDynamic([
{ {
provide: IApiService, provide: IApiService,
deps: [], deps: [],
useValue: serviceObj useValue: serviceObj
},{
provide: LoggerService,
deps: [],
useValue: loggerService
} }
]).bootstrapModule(FrontcraftAppModule) ]).bootstrapModule(FrontcraftAppModule)
.catch(err => console.error(err)); .catch(err => console.error(err));
+1
View File
@@ -1,6 +1,7 @@
{ {
"extends": "./tsconfig.json", "extends": "./tsconfig.json",
"compilerOptions": { "compilerOptions": {
"strict": false,
"outDir": "./out-tsc/app", "outDir": "./out-tsc/app",
"paths": { "paths": {
"@angular/*": [ "@angular/*": [
+7 -7
View File
@@ -1,8 +1,8 @@
import { Injector } from "../src/backend/Injector/Injector"; import { Injector } from "../src/backend/Injector/Injector";
import { FrontworkAdmin } from "../src/backend/Admin/Admin"; import { FrontworkAdmin } from "../src/backend/Admin/Admin";
import { T1, T2, Tiers } from "../src/backend/Types/Items"; import { T2, Tiers } from "../src/backend/Types/Items";
import { RPCSocket } from "rpclibrary"; import { RPCSocket, ConnectedSocket } from "rpclibrary";
import { FrontcraftIfc, Auth, User, FrontcraftFeatureIfc, Raid, Character, Rank, Class, Race, Spec, Signup } from "../src/backend/Types/Types"; import { FrontcraftIfc, Auth, User, FrontcraftFeatureIfc, Raid, Character, Rank, Class, Race, Spec, Signup } from "../src/backend/Types/Types";
import { SpecT } from "../src/backend/Types/PlayerSpecs"; import { SpecT } from "../src/backend/Types/PlayerSpecs";
@@ -120,8 +120,8 @@ describe('Frontcraft', () => {
let auth: Auth, let auth: Auth,
adminUser: User, adminUser: User,
server: FrontworkAdmin, server: FrontworkAdmin,
client: RPCSocket & FrontcraftIfc, client: ConnectedSocket<FrontcraftIfc>,
adminClient: RPCSocket & FrontcraftFeatureIfc, adminClient: ConnectedSocket<FrontcraftFeatureIfc>,
raids: Raid[] = [], raids: Raid[] = [],
users: { [username in string]: { account: User, character: Character, auth: Auth, signup?: Signup, item?: string } } = {} users: { [username in string]: { account: User, character: Character, auth: Auth, signup?: Signup, item?: string } } = {}
@@ -168,12 +168,12 @@ describe('Frontcraft', () => {
}).then(adminUser => { }).then(adminUser => {
client.UserManager.login(adminUser.username, 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb').then(auth => { client.UserManager.login(adminUser.username, 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb').then(auth => {
const sock = new RPCSocket( const sock = new RPCSocket<FrontcraftFeatureIfc>(
auth.port, auth.port,
"localhost" "localhost"
) )
sock.connect<any>(auth.token.value).then(cli => { sock.connect(auth.token.value).then(cli => {
adminClient = cli adminClient = cli
sock.hook('kick', () => { sock.hook('kick', () => {
console.log("I got kicked"); console.log("I got kicked");
@@ -240,7 +240,7 @@ describe('Frontcraft', () => {
it('should sign up', (done) => { it('should sign up', (done) => {
Promise.all(Object.values(users).map((user) => Promise.all(Object.values(users).map((user) =>
adminClient.signup.sign(user.auth.token.value, user.character, raids[0], false).catch(done) adminClient.signup.sign(user.auth.token.value, user.character, raids[0], false, ""+Math.floor(Math.random()*10000)).catch(done)
)).then(x => { )).then(x => {
adminClient.signup.getSignups(raids[0]).then(s => { adminClient.signup.getSignups(raids[0]).then(s => {
if (s.length == testAccounts.length) { if (s.length == testAccounts.length) {