diff --git a/frontworkAdmin.sqlite b/frontworkAdmin.sqlite new file mode 100644 index 0000000..1d8b54c Binary files /dev/null and b/frontworkAdmin.sqlite differ diff --git a/live_db/frontworkAdmin.sqlite b/live_db/frontworkAdmin.sqlite new file mode 100644 index 0000000..67d9c2d Binary files /dev/null and b/live_db/frontworkAdmin.sqlite differ diff --git a/package-lock.json b/package-lock.json index d594e8e..0d3af18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -479,7 +479,8 @@ "bluebird": { "version": "3.5.5", "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": { "version": "1.19.0", @@ -835,9 +836,9 @@ "dev": true }, "colorette": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.0.8.tgz", - "integrity": "sha512-X6Ck90ReaF+EfKdVGB7vdIQ3dr651BbIrBwY5YBKg13fjH+940sTtp7/Pkx33C6ntYfQcRumOs/aUQhaRPpbTQ==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.1.0.tgz", + "integrity": "sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg==" }, "combined-stream": { "version": "1.0.8", @@ -850,7 +851,8 @@ "commander": { "version": "2.20.0", "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": { "version": "1.0.1", @@ -2057,9 +2059,9 @@ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" }, "getopts": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.2.4.tgz", - "integrity": "sha512-Rz7DGyomZjrenu9Jx4qmzdlvJgvrEFHXHvjK0FcZtcTC1U5FmES7OdZHUwMuSnEE6QvBvwse1JODKj7TgbSEjQ==" + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.2.5.tgz", + "integrity": "sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA==" }, "getpass": { "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": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", @@ -2731,27 +2743,37 @@ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" }, "knex": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/knex/-/knex-0.19.2.tgz", - "integrity": "sha512-TVYvlp2esS4LjjJSz8XuE48bPJq4N3lWnETQVgJ3hXPEqjiDjxcTa3bCn6F5ipQuBaMAAaFHNrqsZm7BttogdA==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/knex/-/knex-0.19.5.tgz", + "integrity": "sha512-Hy258avCVircQq+oj3WBqPzl8jDIte438Qlq+8pt1i/TyLYVA4zPh2uKc7Bx0t+qOpa6D42HJ2jjtl2vagzilw==", "requires": { - "bluebird": "^3.5.5", - "colorette": "1.0.8", - "commander": "^2.20.0", + "bluebird": "^3.7.0", + "colorette": "1.1.0", + "commander": "^3.0.2", "debug": "4.1.1", - "getopts": "2.2.4", + "getopts": "2.2.5", "inherits": "~2.0.4", "interpret": "^1.2.0", "liftoff": "3.1.0", "lodash": "^4.17.15", "mkdirp": "^0.5.1", - "pg-connection-string": "2.0.0", + "pg-connection-string": "2.1.0", "tarn": "^2.0.0", "tildify": "2.0.0", - "uuid": "^3.3.2", + "uuid": "^3.3.3", "v8flags": "^3.1.3" }, "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": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3948,9 +3970,9 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pg-connection-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.0.0.tgz", - "integrity": "sha1-Pu/lmX4G2Ugh5NUC5CtqHHP434I=" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.1.0.tgz", + "integrity": "sha512-bhlV7Eq09JrRIvo1eKngpwuqKtJnNhZdpdOlvrPrA4dxqXPjxSrbNrfnIDmTpwMyRszrcV4kU5ZA4mMsQUrjdg==" }, "picomatch": { "version": "2.2.1", @@ -4340,18 +4362,6 @@ "requires": { "expand-tilde": "^2.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": { @@ -4388,13 +4398,21 @@ } }, "rpclibrary": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.9.2.tgz", - "integrity": "sha512-MOtVm0IBRLryXag1IwkYNPVFegbvW10+MbCjhr3GptO3MA7nHZ545ammgoCWnREtGOgFeN4Jib/EBDOJ9Ep41Q==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.10.2.tgz", + "integrity": "sha512-uf4FtylK+tyVo1O8YVihxYxaI4KHnKBzq05SsLYe8ZPXdO8Hqrv7Je5cwv3bfx0FBCO1vVrSc8DT1P6ypGY6uQ==", "requires": { "bsock": "^0.1.9", + "crypto-js": "^4.0.0", "http": "0.0.0", "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": { diff --git a/package.json b/package.json index 8a3ad01..8144cc5 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "version": "1.0.0", "scripts": { "tsc": "tsc", + "knex": "knex", "launch": "node lib/src/backend/Launcher.js", "start": "npm run build && npm run launch", "start-backend": "npm run build-backend && npm run launch", @@ -37,7 +38,6 @@ "git-cherrypicker": "0.0.3", "git-describe": "^4.0.4", "http": "0.0.0", - "knex": "^0.19.2", "loadson": "^1.0.0", "log4js": "^4.5.1", "lowdb": "^1.0.0", @@ -46,7 +46,7 @@ "path": "^0.12.7", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.0", - "rpclibrary": "^1.9.2", + "rpclibrary": "^1.10.2", "simple-git": "^1.124.0", "spawn-sync": "^2.0.0", "sqlite3": "^4.1.1", @@ -58,6 +58,7 @@ "xmlhttprequest": "^1.8.0" }, "devDependencies": { + "knex": "^0.19.5", "madge": "^3.6.0", "mocha": "^7.1.0", "terser-webpack-plugin": "^1.4.1", diff --git a/src/backend/Admin/Admin.ts b/src/backend/Admin/Admin.ts index a94a209..f0e83ff 100644 --- a/src/backend/Admin/Admin.ts +++ b/src/backend/Admin/Admin.ts @@ -4,7 +4,7 @@ import { getLogger, Logger, } from "log4js"; import { promises as fs, mkdirSync } from "fs" import { RPCServer } from 'rpclibrary' import * as Path from 'path' -import * as Knex from 'knex'; +import * as Knex from 'knex'; import * as http from 'http'; import * as express from 'express'; import { GuildManager } from '../Components/Guild/GuildManager'; @@ -36,18 +36,18 @@ getLogger().level = 'debug' PubSub ] }) -export class FrontworkAdmin -implements TableDefinitionExporter, IAdmin { - knex:Knex +export class FrontworkAdmin + implements TableDefinitionExporter, IAdmin { + knex: Knex config: RPCConfigLoader rpcServer: RPCServer private express private httpServer - constructor(private frontworkComponents: FrontworkComponent[] = []){ + constructor(private frontworkComponents: FrontworkComponent[] = []) { this.config = new RPCConfigLoader({ - name: "FrontworkAdminConf", + name: "FrontworkAdminConf", getDefaultConfig: () => { return { httpPort: 8080, @@ -55,57 +55,61 @@ implements TableDefinitionExporter, IAdmin { dbConf: { client: 'sqlite3', 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, } - } + } } - }, './config', this.configChangeHandler) + }, './config', this.configChangeHandler) } - async start(){ + async start() { await this.makeKnex() this.startWebsocket() - await Promise.all( this.frontworkComponents.map(c => c.initialize?c.initialize():undefined )) - getLogger('Admin#start').debug(this.frontworkComponents.length+" components initialized") + await Promise.all(this.frontworkComponents.map(c => c.initialize ? c.initialize() : undefined)) + getLogger('Admin#start').debug(this.frontworkComponents.length + " components initialized") await this.startWebserver() } - stop(){ - Promise.all([ - ...this.frontworkComponents.map(c => c.stop?c.stop():undefined ), + stop() { + Promise.all([ + ...this.frontworkComponents.map(c => c.stop ? c.stop() : undefined), ]) - .catch(e => getLogger('Admin#stop').warn(e)) - .finally(() => { - this.rpcServer.destroy() - process.exit(0) - }) + .catch(e => getLogger('Admin#stop').warn(e)) + .finally(() => { + this.rpcServer.destroy() + process.exit(0) + }) } - protected configChangeHandler = (conf:AdminConf, key?:string) => { - if(key === 'dbConf'){ + protected configChangeHandler = (conf: AdminConf, key?: string) => { + if (key === 'dbConf') { this.makeKnex() } } - getConfigKey(key:string){ + getConfigKey(key: string) { return this.config.getConfigKey(key) } - setConfigKey(key:string, value:any){ + setConfigKey(key: string, value: any) { return this.config.setConfigKey(key, value) } - getTableDefinitions(): TableDefiniton[]{ + getTableDefinitions(): TableDefiniton[] { return [ ...this.frontworkComponents ] - .filter(exp => exp.getTableDefinitions != null) - .flatMap(exp => exp.getTableDefinitions()) + .filter(exp => exp.getTableDefinitions != null) + .flatMap(exp => exp.getTableDefinitions()) } - private startWebsocket(){ + private startWebsocket() { this.rpcServer = new RPCServer(20000, [ ...this.frontworkComponents, { @@ -121,40 +125,41 @@ implements TableDefinitionExporter, IAdmin { getLogger('Admin#startWebsocket').debug("Websocket up on", 20000) } - private async startWebserver(){ - if(this.httpServer != null || this.express != null){ + private async startWebserver() { + if (this.httpServer != null || this.express != null) { getLogger('Admin#startWebserver').warn("Webserver is already running") return } - - let port:number = this.config.getConfig().httpPort + + let port: number = this.config.getConfig().httpPort this.express = express() const distFolder = "../../../../dist" const ngExpressServer = Path.join(__dirname, distFolder, 'server.js') - this.express.get('*.*', (req, res)=>{ + this.express.get('*.*', (req, res) => { //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{ - const req = require(distFolder+"/server.js") + try { + const req = require(distFolder + "/server.js") await req.attachExpress(this.express) - getLogger('Admin#startWebserver').info('Frontend from '+ngExpressServer+" loaded") - }catch(e){ + getLogger('Admin#startWebserver').info('Frontend from ' + ngExpressServer + " loaded") + } catch (e) { getLogger('Admin#startWebserver').error(e) - 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("No angular SSR module was provided in " + ngExpressServer) + getLogger('Admin#startWebserver').warn("This is not fatal, but your page will not render on *" + 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(){ - if(this.httpServer == null || this.express == null){ + private stopWebserver() { + if (this.httpServer == null || this.express == null) { getLogger('Admin#stopWebserver').warn("Webserver is not running") return } @@ -164,38 +169,42 @@ implements TableDefinitionExporter, IAdmin { getLogger('Admin#stopWebserver').info("Webserver stopped") } - async makeKnex():Promise{ - const conf:Knex.Config = this.config.getConfigKey("dbConf") + async makeKnex(): Promise { + const conf: Knex.Config = this.config.getConfigKey("dbConf") - getLogger('Admin#makeKnex').debug("Making new knex:", conf) - if(conf.client === 'sqlite3'){ - mkdirSync(Path.dirname((conf.connection).filename), {recursive: true}) + getLogger('Admin#makeKnex').debug("Making new knex:", conf) + if (conf.client === 'sqlite3') { + mkdirSync(Path.dirname((conf.connection).filename), { recursive: true }) } this.knex = Knex(conf) - if(conf.client === 'sqlite3'){ + if (conf.client === 'sqlite3') { await this.knex.raw('PRAGMA foreign_keys = ON'); - + } await Promise.all( this.getTableDefinitions() - //make unique by name - .filter((other, index, self) => index === self.findIndex( - (self) => self.name === other.name) - ) - //create table if not exists - .map(async (def)=> { - const hasTable = await this.knex.schema.hasTable(def.name) - if(!hasTable) - return await this.knex.schema.createTable(def.name, def.tableBuilder) - }) + //make unique by name + .filter((other, index, self) => index === self.findIndex( + (self) => self.name === other.name) + ) + //create table if not exists + .map(async (def) => { + const hasTable = await this.knex.schema.hasTable(def.name) + if (!hasTable) + 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 } } -process.on( 'SIGINT', function() { - getLogger('process#SIGINT').info("Shutting down from SIGINT (Ctrl-C)" ); +process.on('SIGINT', function () { + getLogger('process#SIGINT').info("Shutting down from SIGINT (Ctrl-C)"); Injector.resolve(FrontworkAdmin).stop() }) diff --git a/src/backend/Components/Item/ItemManager.ts b/src/backend/Components/Item/ItemManager.ts index 0d38aa3..c43d118 100644 --- a/src/backend/Components/Item/ItemManager.ts +++ b/src/backend/Components/Item/ItemManager.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from "../../Injector/ServiceDecorator"; import { ItemManagerFeatureIfc, ItemManagerIfc } from "./RPCInterface"; import { FrontworkComponent } from "../../Types/FrontworkComponent"; 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 { IItemManager } from "./Interface"; import { IUserManager } from "../User/Interface"; @@ -171,7 +171,7 @@ export class ItemManager table.boolean('hidden').defaultTo(false).notNullable() table.string('tooltip').notNullable() table.enu('tier', _Tiers).notNullable() - table.json('stats').notNullable() + table.json('stats') } }] } diff --git a/src/backend/Components/PubSub/Interface.ts b/src/backend/Components/PubSub/Interface.ts index 86aab0c..96e9cd9 100644 --- a/src/backend/Components/PubSub/Interface.ts +++ b/src/backend/Components/PubSub/Interface.ts @@ -1,7 +1,6 @@ -import { SubscriptionResponse } from "rpclibrary" export class IPubSub{ publish: (topic: string, p: UpdateType) => Promise - subscribe: (topic:string, callback: (p:UpdateType)=>any) => Promise + subscribe: (topic:string, callback: (p:UpdateType)=>any) => Promise unsubscribe: (uuid: string) => Promise -} +} \ No newline at end of file diff --git a/src/backend/Components/PubSub/PubSub.ts b/src/backend/Components/PubSub/PubSub.ts index 3764086..0ce7604 100644 --- a/src/backend/Components/PubSub/PubSub.ts +++ b/src/backend/Components/PubSub/PubSub.ts @@ -1,6 +1,7 @@ import { Injectable } from "../../Injector/ServiceDecorator"; -import { SubscriptionResponse, makeSubResponse, RPCExporter } from "rpclibrary"; +import { RPCExporter } from "rpclibrary"; import { IPubSub } from "./Interface"; +const uuidv4 = require('uuid/v4') @Injectable(IPubSub) export class PubSub @@ -34,13 +35,11 @@ implements RPCExporter{ })) } - subscribe = async (topic:string, callback:(p:UpdateType)=>any) : Promise> => { - const resp = makeSubResponse({ - topic: topic - }) + subscribe = async (topic:string, callback:(p:UpdateType)=>any) : Promise => { + const uuid = uuidv4() if(!this.subs[topic]) this.subs[topic] = {} - this.subs[topic][resp.uuid] = callback - return resp + this.subs[topic][uuid] = callback + return uuid } private unsubscribe = async (uuid: string) => { diff --git a/src/backend/Components/Raid/Interface.ts b/src/backend/Components/Raid/Interface.ts index ee89249..6984188 100644 --- a/src/backend/Components/Raid/Interface.ts +++ b/src/backend/Components/Raid/Interface.ts @@ -6,7 +6,7 @@ export class IRaidManager{ addSignup: (signup: Signup) => Promise removeSignup: (signup: Signup) => Promise getSignups: (raid:Raid) => Promise<(Signup & Character & Spec & User)[]> - sign: (userToken: string, character:Character, raid:Raid, late:boolean) => Promise + sign: (userToken: string, character:Character, raid:Raid, late:boolean, memo?: string) => Promise unsign: (userToken: string, character:Character, raid:Raid,) => Promise archiveRaid: (raid:Raid) => Promise getRaidData: (raid:Raid) => Promise diff --git a/src/backend/Components/Raid/RaidManager.ts b/src/backend/Components/Raid/RaidManager.ts index ac5bd06..5ac5a01 100644 --- a/src/backend/Components/Raid/RaidManager.ts +++ b/src/backend/Components/Raid/RaidManager.ts @@ -7,9 +7,11 @@ import { IRaidManager } from "./Interface"; import { IUserManager } from "../User/Interface"; import { ICharacterManager } from "../Character/Interface"; import { _Tiers } from "../../Types/Items"; +import { MAX_MEMO_LENGTH } from "../../Types/Constants"; import { IItemManager } from "../Item/Interface"; import { ItemManager } from "../Item/ItemManager"; import { IPubSub } from "../PubSub/Interface"; +import { getLogger } from "log4js"; @Injectable(IRaidManager) export class RaidManager @@ -89,6 +91,8 @@ export class RaidManager table.foreign('characterid').references('id').inTable('characters').onDelete('CASCADE') table.boolean('benched').defaultTo('false') 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}) => { const data = await this.getRaidData(raid) - this.pubsub.publish(""+raid.id, data) - await this.notifyRaids() + try{ + await this.pubsub.publish(String(raid.id), data) + await this.notifyRaids() + }catch(e){ + getLogger('RaidManager#notifyRaid').debug(e); + } } 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 => { @@ -158,7 +170,7 @@ export class RaidManager await Promise.all([ ...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() @@ -169,10 +181,10 @@ export class RaidManager archiveRaid = async (raid: Raid): Promise => { 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') - //.transacting(tx) + .transacting(tx) .insert({ id: raidData.id, raiddata: JSON.stringify(raidData) @@ -182,7 +194,7 @@ export class RaidManager Object.values(raidData.participants).flat().flatMap((p: (Signup & Character & Spec)) => this.admin .knex(raid.tier + 'tokens') - //.transacting(tx) + .transacting(tx) .where({ characterid: p.characterid, signupid: null @@ -191,10 +203,10 @@ export class RaidManager )) await this.admin.knex('raids') - //.transacting(tx) + .transacting(tx) .where('id', '=', raid.id) .del() - //await tx.commit() + await tx.commit() const row = await this.admin.knex('archive') @@ -242,7 +254,6 @@ export class RaidManager healers: <(Signup & Character & Spec)[]>[], tanks: <(Signup & Character & Spec)[]>[] } - //const tx = await this.admin.knex.transaction() const subQuery = this.admin .knex('signups') @@ -257,14 +268,12 @@ export class RaidManager const raidInDb: Raid = await this.admin.knex('raids') .select('*', subQuery) - //.transacting(tx) .where('id', '=', raid.id) .first() const characterData: (Signup & Character & Spec)[] = await this.admin .knex('signups as s') - //.transacting(tx) - .select('s.id as id', 'charactername', 'rank', 'class', 'specid', 'specname', 'race', 'userid', 'benched', 'late', 'raidid', 'characterid', 'specid') + .select('s.id as id', 'charactername', 'rank', 'class', 'specid', 'specname', 'race', 'userid', 'benched', 'late', 'raidid', 'characterid', 'specid', 'memo', 'timestamp') .join('raids as r', 's.raidid', '=', 'r.id') .join('characters as c', 's.characterid', '=', 'c.id') .join('users as u', 'c.userid', '=', 'u.id') @@ -285,7 +294,6 @@ export class RaidManager const tokenData: (Character & SRToken & Item)[] = await this.admin .knex('signups as s') - //.transacting(tx) .select('*', 's.id as id') .join('raids as r', 's.raidid', '=', 'r.id') .join('characters as c', 's.characterid', '=', 'c.id') @@ -300,8 +308,6 @@ export class RaidManager this.whereNotNull('t.signupid') }) - //await tx.commit() - tokenData.forEach(data => { if (!raiddata.tokens[data.itemname]) raiddata.tokens[data.itemname] = [] @@ -338,16 +344,15 @@ export class RaidManager .select('*', 'si.id as 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) if (!maybeUserRecord || maybeUserRecord.user.id != character.userid) { 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 .knex('signups') - //.transacting(tx) .select('*') .where({ raidid: raid.id!, @@ -358,17 +363,16 @@ export class RaidManager if (!exists) { await this.admin .knex('signups') - //.transacting(tx) .insert({ raidid: raid.id!, characterid: character.id!, late: late, benched: false, + memo: memo }) } else { await this.admin .knex('signups') - //.transacting(tx) .where({ id: exists.id }) @@ -377,9 +381,9 @@ export class RaidManager characterid: character.id!, late: late, benched: false, + memo: memo }) } - //await tx.commit() await this.notifyRaid(raid) @@ -405,13 +409,6 @@ export class RaidManager adminUnsign = async (character: Character, raid: Raid) => { 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) //check if token has to be deleted diff --git a/src/backend/Components/Shoutbox/Interface.ts b/src/backend/Components/Shoutbox/Interface.ts index 33eb3d4..6740386 100644 --- a/src/backend/Components/Shoutbox/Interface.ts +++ b/src/backend/Components/Shoutbox/Interface.ts @@ -1,5 +1,4 @@ import { Raid, Signup, Character, RaidData } from "../../Types/Types" -import { SubscriptionResponse } from "rpclibrary" export type ShoutMessage = { message: string, @@ -10,5 +9,5 @@ export type ShoutMessage = { export class IShoutbox{ shout: (uuid:string, msg: ShoutMessage) => Promise getFeed: () => Promise - subscribe: (callback) => Promise + subscribe: (callback) => Promise } diff --git a/src/backend/Components/Shoutbox/Shoutbox.ts b/src/backend/Components/Shoutbox/Shoutbox.ts index 904b19d..b67126d 100644 --- a/src/backend/Components/Shoutbox/Shoutbox.ts +++ b/src/backend/Components/Shoutbox/Shoutbox.ts @@ -3,8 +3,8 @@ import { ShoutboxFeatureIfc, ShoutboxIfc } from "./RPCInterface"; import { FrontworkComponent } from "../../Types/FrontworkComponent"; import { TableDefiniton } from "../../Types/Types"; import { IShoutbox, ShoutMessage } from "./Interface"; -import { SubscriptionResponse, makeSubResponse } from "rpclibrary"; import * as CircularBuffer from "circular-buffer"; +const uuid = require('uuid/v4') @Injectable(IShoutbox) export class Shoutbox @@ -52,10 +52,10 @@ implements FrontworkComponent, IShoutbox{ return this.log.toarray() } - subscribe = async (callback) : Promise => { - const resp = makeSubResponse({}) - this.subs[resp.uuid] = callback - return resp + subscribe = async (callback) : Promise => { + uuid() + this.subs[uuid] = callback + return uuid } unsubscribe = async (uuid: string) => { diff --git a/src/backend/Components/User/UserManager.ts b/src/backend/Components/User/UserManager.ts index 8bdd9c1..4e73e5e 100644 --- a/src/backend/Components/User/UserManager.ts +++ b/src/backend/Components/User/UserManager.ts @@ -6,10 +6,10 @@ import { RaidManager } from "../Raid/RaidManager"; import { CharacterManager } from "../Character/CharacterManager"; import { UserManagerFeatureIfc, UserManagerIfc } from "./RPCInterface"; import { FrontworkComponent } from "../../Types/FrontworkComponent"; -import { Rank, User, Auth, _Rank, TableDefiniton, RPCPermission, FrontcraftFeatureIfc, AnyRPCExporter, Token, UserRecord } from "../../Types/Types"; +import { Rank, User, Auth, _Rank, TableDefiniton, RPCPermission, FrontcraftFeatureIfc, Token, UserRecord } from "../../Types/Types"; import { IAdmin } from "../../Admin/Interface"; import { IUserManager } from "./Interface"; -import { getLogger, Logger } from "log4js"; +import { getLogger } from "log4js"; import { saltedHash } from "../../Util/hash"; import { _Tiers, Tiers } from "../../Types/Items"; import { ICharacterManager } from "../Character/Interface"; @@ -24,8 +24,8 @@ const uuid = require('uuid/v4') const salt = "6pIbc6yjSN" const ONE_WEEK = 604800000 -type Serverstate = { - server: RPCServer, +type Serverstate = { + server: RPCServer, port: number, }; @@ -51,7 +51,7 @@ export class UserManager private character: CharacterManager exporters: any[] = [] - rankServer: Serverstate<{}, FrontcraftFeatureIfc> + rankServer: Serverstate userLogins: { [username in string]: UserRecord } = {} allowed: string[] = [] @@ -376,12 +376,12 @@ export class UserManager return } - startRankServer = async (port: number): Promise> => { + startRankServer = async (port: number): Promise> => { let rpcs = [ ...this.exportRPCFeatures(), ...this.exporters.flatMap((exp) => exp.exportRPCFeatures()) ] - let rpcServer = new RPCServer<{}, FrontcraftFeatureIfc>(port, rpcs, { + let rpcServer = new RPCServer(port, rpcs, { accessFilter: async (sesame, exporter) => { const record = this.getUserRecordByToken(sesame!) if (!record) return false diff --git a/src/backend/Injector/ServiceDecorator.ts b/src/backend/Injector/ServiceDecorator.ts index 0891bd9..64e47cf 100644 --- a/src/backend/Injector/ServiceDecorator.ts +++ b/src/backend/Injector/ServiceDecorator.ts @@ -1,6 +1,5 @@ import { Injector } from "./Injector"; import { Type, GenericClassDecorator } from "./Util"; -import { FrontworkComponent } from "../Types/FrontworkComponent"; import { RPCExporter } from "rpclibrary"; /** diff --git a/src/backend/Types/Constants.ts b/src/backend/Types/Constants.ts new file mode 100644 index 0000000..9bac1c2 --- /dev/null +++ b/src/backend/Types/Constants.ts @@ -0,0 +1 @@ +export const MAX_MEMO_LENGTH = 255 \ No newline at end of file diff --git a/src/backend/Types/FrontworkComponent.ts b/src/backend/Types/FrontworkComponent.ts index 185285b..53e681c 100644 --- a/src/backend/Types/FrontworkComponent.ts +++ b/src/backend/Types/FrontworkComponent.ts @@ -1,4 +1,4 @@ -import { RPCInterface, RPCExporter, RPCInterfaceArray } from "rpclibrary"; +import { RPCInterface, RPCExporter, RPCDefinitions } from "rpclibrary"; import { PrivilegedRPCExporter } from "./PrivilegedRPCExporter"; import { TableDefinitionExporter } from "./Interfaces"; import { TableDefiniton } from "./Types"; @@ -8,15 +8,14 @@ export interface FrontworkComponent< FeatureIfc extends RPCInterface = RPCInterface, Name extends keyof Ifc = keyof Ifc, FeatureName extends keyof FeatureIfc = keyof FeatureIfc, - SubresT = {} > extends - PrivilegedRPCExporter, + PrivilegedRPCExporter, TableDefinitionExporter { name: Name; - exportRPCFeatures(): RPCExporter[] - exportRPCs(): RPCInterfaceArray[Name] + exportRPCFeatures(): RPCExporter[] + exportRPCs(): RPCDefinitions[Name] getTableDefinitions(): TableDefiniton[] initialize?(): Promise diff --git a/src/backend/Types/Plugin.ts b/src/backend/Types/Plugin.ts index 6b11aa8..717ead1 100644 --- a/src/backend/Types/Plugin.ts +++ b/src/backend/Types/Plugin.ts @@ -6,7 +6,7 @@ import { TableDefiniton } from "./Types" export abstract class Plugin -implements ConfigExporter, RPCExporter, TableDefinitionExporter{ +implements ConfigExporter, RPCExporter, TableDefinitionExporter{ constructor (protected admin: FrontworkAdmin, public name:string){ if(!this.getConfig()){ diff --git a/src/backend/Types/PrivilegedRPCExporter.ts b/src/backend/Types/PrivilegedRPCExporter.ts index fbc612b..94eff11 100644 --- a/src/backend/Types/PrivilegedRPCExporter.ts +++ b/src/backend/Types/PrivilegedRPCExporter.ts @@ -1,11 +1,10 @@ -import { RPCExporter, RPCInterface, RPC, RPCInterfaceArray } from "rpclibrary"; +import { RPCExporter, RPCInterface } from "rpclibrary"; export interface PrivilegedRPCExporter < Ifc extends RPCInterface = RPCInterface, FeatureIfc extends RPCInterface = RPCInterface, Name extends keyof Ifc = keyof Ifc, FeatureName extends keyof FeatureIfc = keyof FeatureIfc, - SubresT = {} >extends RPCExporter{ - exportRPCFeatures() : RPCExporter[] + exportRPCFeatures() : RPCExporter[] } \ No newline at end of file diff --git a/src/backend/Types/Types.ts b/src/backend/Types/Types.ts index 6aed6d5..3c9e7e8 100644 --- a/src/backend/Types/Types.ts +++ b/src/backend/Types/Types.ts @@ -165,6 +165,8 @@ export type Signup = { characterid: number benched: boolean late: boolean + timestamp: string + memo?: string } export type Character = { diff --git a/src/frontend/angular.json b/src/frontend/angular.json index e01c8c6..138d77f 100644 --- a/src/frontend/angular.json +++ b/src/frontend/angular.json @@ -58,6 +58,9 @@ "configurations": { "production": { "aot": true, + "optimization": true, + "extractCss": true, + "sourceMap": true, "fileReplacements": [ { "replace": "src/environments/environment.ts", diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 501c496..227c0cb 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -15742,13 +15742,21 @@ "integrity": "sha512-ZYzRkETgBrdEGzL5JSKimvjI2CX7ioyZCkX2BpcfyjqI+079W0wHAyj5W4rIZMcDSOHgLZtgz1IdDi/vU77KEQ==" }, "rpclibrary": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.9.2.tgz", - "integrity": "sha512-MOtVm0IBRLryXag1IwkYNPVFegbvW10+MbCjhr3GptO3MA7nHZ545ammgoCWnREtGOgFeN4Jib/EBDOJ9Ep41Q==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.10.2.tgz", + "integrity": "sha512-uf4FtylK+tyVo1O8YVihxYxaI4KHnKBzq05SsLYe8ZPXdO8Hqrv7Je5cwv3bfx0FBCO1vVrSc8DT1P6ypGY6uQ==", "requires": { "bsock": "^0.1.9", + "crypto-js": "^4.0.0", "http": "0.0.0", "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": { diff --git a/src/frontend/package.json b/src/frontend/package.json index 6db2b43..5f231b0 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -29,7 +29,8 @@ "prepush": "npm run lint:ci", "release:changelog": "npm run conventional-changelog -- -p angular -i CHANGELOG.md -s", "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": { "@agm/core": "^1.0.0-beta.5", @@ -75,7 +76,7 @@ "normalize.css": "6.0.0", "pace-js": "1.0.2", "roboto-fontface": "0.8.0", - "rpclibrary": "^1.9.2", + "rpclibrary": "^1.10.2", "rxjs": "6.5.2", "rxjs-compat": "6.3.0", "socicon": "3.0.5", diff --git a/src/frontend/server.ts b/src/frontend/server.ts index ba115a5..e11ec07 100644 --- a/src/frontend/server.ts +++ b/src/frontend/server.ts @@ -13,7 +13,7 @@ global['document'] = win.document; global['alert'] = console.log 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); enableProdMode(); @@ -21,7 +21,7 @@ export async function attachExpress(app, staticDir = "./dist") { const bundle = require(staticDir + '/server/main'); const ServerApiService = bundle.ServerApiService - const serviceObj = new ServerApiService() + const serviceObj = new ServerApiService(loggerService) await serviceObj.initialize() app.set('view engine', 'html'); @@ -31,10 +31,15 @@ export async function attachExpress(app, staticDir = "./dist") { bootstrap: bundle.AppServerModuleNgFactory, providers: [ provideModuleMap(bundle.LAZY_MODULE_MAP), + { + provide: bundle.LoggerService, + deps: [], + useValue: loggerService + }, { provide: bundle.IApiService, + deps: [], useValue: serviceObj, - deps: [] } ] }) diff --git a/src/frontend/src/app/@theme/components/header/header.component.html b/src/frontend/src/app/@theme/components/header/header.component.html index f0c2ec0..f60c2c7 100644 --- a/src/frontend/src/app/@theme/components/header/header.component.html +++ b/src/frontend/src/app/@theme/components/header/header.component.html @@ -29,7 +29,8 @@ - diff --git a/src/frontend/src/app/@theme/components/header/header.component.ts b/src/frontend/src/app/@theme/components/header/header.component.ts index 3e7b7b3..65565bf 100644 --- a/src/frontend/src/app/@theme/components/header/header.component.ts +++ b/src/frontend/src/app/@theme/components/header/header.component.ts @@ -5,7 +5,7 @@ import { LayoutService } from '../../../@core/utils'; import { map, takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; import { User } from '../../../../../../backend/Types/Types'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute, NavigationEnd } from '@angular/router'; import { ChatComponent } from './chat.component'; import { ShoutMessage } from '../../../../../../backend/Components/Shoutbox/Interface'; import { IApiService } from '../../../frontcraft/services/ApiService'; @@ -41,7 +41,7 @@ export class HeaderComponent implements OnInit, OnDestroy { ]; currentTheme = 'default'; - userMenu : NbMenuItem[] = [ { title: 'Log out', link: '/auth/logout' } ]; + userMenu: NbMenuItem[] = []; sendMessage: any = console.log chatwindow: ChatComponent @@ -50,13 +50,29 @@ export class HeaderComponent implements OnInit, OnDestroy { lastmessage = "asdasd" modifyPermissions - constructor(private sidebarService: NbSidebarService, - private router: Router, - private themeService: NbThemeService, - private layoutService: LayoutService, - private api: IApiService, - private dialogService : NbDialogService - ) {} + constructor( + private sidebarService: NbSidebarService, + private router: Router, + private themeService: NbThemeService, + private layoutService: LayoutService, + 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() { this.themeService.changeTheme("dark"); @@ -64,28 +80,36 @@ export class HeaderComponent implements OnInit, OnDestroy { this.currentTheme = this.themeService.currentTheme; this.modifyPermissions = this.api.get('modifyPermissions') 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.title = info.name }) this.api.connectShoutbox((msg) => { this.chatlog.push(msg) - msg['reply']=false + msg['reply'] = false - if(msg.message != this.lastmessage){ + if (msg.message != this.lastmessage) { 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 => { this.sendMessage = (msg) => { sendMsg(msg) 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() @@ -96,14 +120,14 @@ export class HeaderComponent implements OnInit, OnDestroy { .subscribe(themeName => this.currentTheme = themeName); } - openChat(){ + openChat() { this.newmessage = false const ref = this.dialogService.open(ChatComponent, { context: { sendMessage: this.sendMessage, } }); - ref.onClose.subscribe(()=>{ + ref.onClose.subscribe(() => { this.chatwindow = null }) this.chatwindow = ref.componentRef.instance @@ -122,7 +146,6 @@ export class HeaderComponent implements OnInit, OnDestroy { toggleSidebar(): boolean { this.sidebarService.toggle(true, 'menu-sidebar'); this.layoutService.changeLayoutSize(); - return false; } diff --git a/src/frontend/src/app/app.component.ts b/src/frontend/src/app/app.component.ts index e8b735c..7a00e3d 100644 --- a/src/frontend/src/app/app.component.ts +++ b/src/frontend/src/app/app.component.ts @@ -9,9 +9,4 @@ import { Component, OnInit } from '@angular/core'; selector: 'ngx-app', template: '', }) -export class AppComponent implements OnInit { - - constructor() { } - - ngOnInit(): void { } -} +export class AppComponent { } diff --git a/src/frontend/src/app/app.module.ts b/src/frontend/src/app/app.module.ts index 95b70f8..e4e65db 100644 --- a/src/frontend/src/app/app.module.ts +++ b/src/frontend/src/app/app.module.ts @@ -5,11 +5,15 @@ */ import { BrowserModule } from '@angular/platform-browser'; 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 { ThemeModule } from './@theme/theme.module'; import { AppComponent } from './app.component'; 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 { NbChatModule, NbDatepickerModule, @@ -19,10 +23,6 @@ import { NbToastrModule, NbWindowModule, } 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({ declarations: [ diff --git a/src/frontend/src/app/app.server.module.ts b/src/frontend/src/app/app.server.module.ts index 396230d..7fbde1e 100644 --- a/src/frontend/src/app/app.server.module.ts +++ b/src/frontend/src/app/app.server.module.ts @@ -1,5 +1,5 @@ -import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { NgModule } from '@angular/core'; import { ServerModule } from '@angular/platform-server'; import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader'; diff --git a/src/frontend/src/app/frontcraft/UpdatingComponent.ts b/src/frontend/src/app/frontcraft/UpdatingComponent.ts new file mode 100644 index 0000000..018c8c3 --- /dev/null +++ b/src/frontend/src/app/frontcraft/UpdatingComponent.ts @@ -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 + } + } +} \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/auth/auth-layout.component.ts b/src/frontend/src/app/frontcraft/auth/auth-layout.component.ts index 9fb2fc8..3d5e9ff 100644 --- a/src/frontend/src/app/frontcraft/auth/auth-layout.component.ts +++ b/src/frontend/src/app/frontcraft/auth/auth-layout.component.ts @@ -1,6 +1,4 @@ import { Component, OnInit } from '@angular/core'; -import { ClientApiService } from '../services/client-login-api'; -import { Router } from '@angular/router'; @Component({ selector: 'auth-layout', diff --git a/src/frontend/src/app/frontcraft/auth/login/login.component.html b/src/frontend/src/app/frontcraft/auth/login/login.component.html index 18b17a8..859fe57 100644 --- a/src/frontend/src/app/frontcraft/auth/login/login.component.html +++ b/src/frontend/src/app/frontcraft/auth/login/login.component.html @@ -48,5 +48,5 @@
- Don't have an account? Register + Don't have an account? Register
diff --git a/src/frontend/src/app/frontcraft/auth/login/login.component.ts b/src/frontend/src/app/frontcraft/auth/login/login.component.ts index afdc769..a4f8c1b 100644 --- a/src/frontend/src/app/frontcraft/auth/login/login.component.ts +++ b/src/frontend/src/app/frontcraft/auth/login/login.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { ClientApiService } from '../../services/client-login-api'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; import { IApiService } from '../../services/ApiService'; import { NbToastrService } from '@nebular/theme'; @@ -24,7 +23,8 @@ export class MyLoginComponent implements OnInit{ constructor( private router : Router, private api : IApiService, - private toastr: NbToastrService + private toastr: NbToastrService, + private route: ActivatedRoute ){} ngOnInit(){ @@ -41,8 +41,9 @@ export class MyLoginComponent implements OnInit{ this.toastr.danger("Login failed", "Error") return }else{ - window['r'] = this.router - this.router.navigate(["/froncraft"]) + this.route.queryParamMap.subscribe(params => { + this.router.navigate([params.get('redirect')]) + }) } } } \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/auth/logout/logout.component.html b/src/frontend/src/app/frontcraft/auth/logout/logout.component.html index 7f6410f..68fb4b9 100644 --- a/src/frontend/src/app/frontcraft/auth/logout/logout.component.html +++ b/src/frontend/src/app/frontcraft/auth/logout/logout.component.html @@ -1 +1 @@ -You will be redirected shortly. If not click here click \ No newline at end of file +You will be redirected shortly. If not click here click \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/auth/logout/logout.component.ts b/src/frontend/src/app/frontcraft/auth/logout/logout.component.ts index de58500..693eead 100644 --- a/src/frontend/src/app/frontcraft/auth/logout/logout.component.ts +++ b/src/frontend/src/app/frontcraft/auth/logout/logout.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { IApiService } from '../../services/ApiService'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; @Component({ selector: 'mylogout', @@ -10,12 +10,15 @@ export class LogoutComponent implements OnInit{ constructor( private api : IApiService, - private router: Router + private router: Router, + private route: ActivatedRoute ){} ngOnInit(){ this.api.logout().then(() => { - this.router.navigate(['/auth/login']) + this.route.queryParamMap.subscribe(params => { + this.router.navigate([params.get('redirect')]) + }) }) } } \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/auth/register/register.component.html b/src/frontend/src/app/frontcraft/auth/register/register.component.html index ea056ec..da18425 100644 --- a/src/frontend/src/app/frontcraft/auth/register/register.component.html +++ b/src/frontend/src/app/frontcraft/auth/register/register.component.html @@ -92,5 +92,5 @@
- Back to Login + Go to Login
diff --git a/src/frontend/src/app/frontcraft/auth/register/register.component.ts b/src/frontend/src/app/frontcraft/auth/register/register.component.ts index b56f6f4..9472538 100644 --- a/src/frontend/src/app/frontcraft/auth/register/register.component.ts +++ b/src/frontend/src/app/frontcraft/auth/register/register.component.ts @@ -1,9 +1,7 @@ import { Component, OnInit } from '@angular/core'; -import { ClientApiService as ClientApiService } from '../../services/client-login-api'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; import { _Rank, _Class, Class, User, _Race } from '../../../../../../backend/Types/Types' import { specs } from '../../../../../../backend/Types/PlayerSpecs' -import { race } from 'rxjs'; import { hash, IApiService } from '../../services/ApiService'; @@ -33,7 +31,8 @@ export class RegisterComponent implements OnInit{ submitted = false constructor( - private router : Router, + private router : Router, + private route : ActivatedRoute, private api : IApiService ){} @@ -84,6 +83,11 @@ export class RegisterComponent implements OnInit{ userid: this.api.getAuth().user.id!, race: char.race }) + + this.route.queryParamMap.subscribe(params => { + this.router.navigate([params.get('redirect')]) + }) + }catch(e){ alert("Error creating character"+e) return diff --git a/src/frontend/src/app/frontcraft/pages/armory/armory.component.ts b/src/frontend/src/app/frontcraft/pages/armory/armory.component.ts index d8b1437..709a132 100644 --- a/src/frontend/src/app/frontcraft/pages/armory/armory.component.ts +++ b/src/frontend/src/app/frontcraft/pages/armory/armory.component.ts @@ -2,7 +2,6 @@ import { Component, OnInit } from '@angular/core'; import { Item, Stats } from '../../../../../../backend/Types/Types'; import { NbToastrService } from '@nebular/theme'; -import { ClientApiService } from '../../services/client-login-api'; import { IApiService } from '../../services/ApiService'; @Component({ diff --git a/src/frontend/src/app/frontcraft/pages/character/character.component.ts b/src/frontend/src/app/frontcraft/pages/character/character.component.ts index 9999a05..0728bd5 100644 --- a/src/frontend/src/app/frontcraft/pages/character/character.component.ts +++ b/src/frontend/src/app/frontcraft/pages/character/character.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit, Input } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { ClientApiService as ClientApiService } from '../../services/client-login-api'; +import { ActivatedRoute } from '@angular/router'; import { Spec, User, Character, Item } from '../../../../../../backend/Types/Types'; import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; import { _Tiers } from '../../../../../../backend/Types/Items'; diff --git a/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts b/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts index 8476eb4..e498fd1 100644 --- a/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts +++ b/src/frontend/src/app/frontcraft/pages/pages-layout.component.ts @@ -1,10 +1,8 @@ -import { Component, AfterContentInit, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { NbMenuItem } from '@nebular/theme'; -import { ClientApiService } from '../services/client-login-api'; import { Router } from '@angular/router'; import { IApiService } from '../services/ApiService'; -declare const Cookies @Component({ selector: 'page-layout', @@ -16,7 +14,7 @@ declare const Cookies `, }) -export class PagesLayoutComponent implements OnInit{ +export class PagesLayoutComponent{ menu:NbMenuItem[] = [{ icon: 'image', @@ -36,18 +34,6 @@ export class PagesLayoutComponent implements OnInit{ link: '/frontcraft/armory' }] - constructor( - private api : IApiService, - private router : Router - ){} + constructor(){} - ngOnInit(): void { - /* - const u = this.api.getCurrentUser() - - if(!u){ - this.router.navigate(['/auth/login']) - } - */ - } } \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/pages.module.ts b/src/frontend/src/app/frontcraft/pages/pages.module.ts index d1c5135..dbfc2a8 100644 --- a/src/frontend/src/app/frontcraft/pages/pages.module.ts +++ b/src/frontend/src/app/frontcraft/pages/pages.module.ts @@ -9,7 +9,6 @@ import { NbInputModule, NbMenuModule, NbCardModule, - NbTreeGridModule, NbListModule, NbTabsetModule, NbIconModule, diff --git a/src/frontend/src/app/frontcraft/pages/raid/archive.component.ts b/src/frontend/src/app/frontcraft/pages/raid/archive.component.ts index 40ad99c..1ad3263 100644 --- a/src/frontend/src/app/frontcraft/pages/raid/archive.component.ts +++ b/src/frontend/src/app/frontcraft/pages/raid/archive.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { ClientApiService as ClientApiService } from '../../services/client-login-api'; import { RaidData } from '../../../../../../backend/Types/Types'; import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; import { IApiService } from '../../services/ApiService'; @@ -34,7 +33,7 @@ export class FrontcraftArchiveComponent implements OnInit{ tanks:[], healers: [] } - tokens = {} + tokens: {[key in string]:any[]} = {} displayedtokens = {} search = "" @@ -53,6 +52,10 @@ export class FrontcraftArchiveComponent implements OnInit{ const raidManager = this.api.get('RaidManager') const raiddata = await raidManager.getArchiveRaid(parseInt(param)) + + console.log(raiddata); + + this.raid = raiddata this.isTier = raiddata.tier != null this.tokens = raiddata.tokens @@ -69,8 +72,18 @@ export class FrontcraftArchiveComponent implements OnInit{ this.changeSearch() } - changeSearch = () => { - this.tokens = this.raid.tokens; - this.displayedtokens = this.raid.tokens; + changeSearch(){ + if(!this.search || this.search == ""){ + 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 + }) + } } } \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.html b/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.html index f590630..b3a281d 100644 --- a/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.html +++ b/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.html @@ -3,34 +3,41 @@ Pick Character -
- - + +
+
+ + + {{character.charactername}} -
-
-
-
- - -   -   -   - -
+ + + +   +   +   + + +
\ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.ts b/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.ts index 2c752cb..5036c2e 100644 --- a/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.ts +++ b/src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.ts @@ -1,7 +1,6 @@ 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 { ClientApiService } from '../../services/client-login-api'; import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; import { IApiService } from '../../services/ApiService'; @@ -13,6 +12,7 @@ export class FrontcraftCharacerpickerComponent implements OnInit{ raid : RaidData characters: Character[] + memo: string constructor( protected dialogRef: NbDialogRef, @@ -25,7 +25,7 @@ export class FrontcraftCharacerpickerComponent implements OnInit{ const signup = this.api.get('signup') 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.dialogRef.close() } diff --git a/src/frontend/src/app/frontcraft/pages/raid/raid.component.html b/src/frontend/src/app/frontcraft/pages/raid/raid.component.html index 03cd5ab..6c9b3fb 100644 --- a/src/frontend/src/app/frontcraft/pages/raid/raid.component.html +++ b/src/frontend/src/app/frontcraft/pages/raid/raid.component.html @@ -5,34 +5,47 @@

- {{raid.title}}

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

-

+

{{raid.description}}

-
- -

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

+

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

+

- - + + + + @@ -46,7 +59,6 @@ -
Tanks ({{raid.tanks.length}}) @@ -63,11 +75,28 @@ + [src]="'/assets/images/'+participant.race.toLowerCase()+'_xs.gif'" /> + [src]="'/assets/images/'+participant.class.toLowerCase()+'.png'" /> {{ participant.charactername }} + +
+

+ {{participant.memo}} +

+ {{participant.timestamp | date : 'HH:mm EEE MMM d'}} +
+
+ + + + Trial @@ -90,11 +119,27 @@ + [src]="'/assets/images/'+participant.race.toLowerCase()+'_xs.gif'" /> + [src]="'/assets/images/'+participant.class.toLowerCase()+'.png'" /> {{ participant.charactername }} + +
+

+ {{participant.memo}} +

+ {{participant.timestamp | date : 'HH:mm EEE MMM d'}} +
+
+ + + + Trial @@ -119,11 +164,27 @@ style="text-transform: capitalize;" [routerLink]="'/frontcraft/character/'+participant.charactername"> + [src]="'/assets/images/'+participant.race.toLowerCase()+'_xs.gif'" /> + [src]="'/assets/images/'+participant.class.toLowerCase()+'.png'" /> {{ participant.charactername }} + +
+

+ {{participant.memo}} +

+ {{participant.timestamp | date : 'HH:mm EEE MMM d'}} +
+
+ + + + Trial @@ -147,7 +208,8 @@ - + @@ -161,15 +223,13 @@
-
+ - + Trial
- + diff --git a/src/frontend/src/app/frontcraft/pages/raid/raid.component.ts b/src/frontend/src/app/frontcraft/pages/raid/raid.component.ts index fd99314..4f0ddf1 100644 --- a/src/frontend/src/app/frontcraft/pages/raid/raid.component.ts +++ b/src/frontend/src/app/frontcraft/pages/raid/raid.component.ts @@ -1,19 +1,22 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { ActivatedRoute, Router, NavigationStart } from '@angular/router'; -import { ClientApiService as ClientApiService } from '../../services/client-login-api'; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; 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 { getClassColor, SpecT } from '../../../../../../backend/Types/PlayerSpecs'; +import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs'; import { FrontcraftBuyTokenComponent } from '../shop/buytoken.component'; import { allItems } from '../../../../../../backend/Types/Items'; import { IApiService } from '../../services/ApiService'; +import { UpdatingComponent } from '../../UpdatingComponent'; +import { LoggerService } from '../../services/logger.service'; @Component({ selector: 'raid', templateUrl: './raid.component.html', }) -export class FrontcraftRaidComponent implements OnInit, OnDestroy{ +export class FrontcraftRaidComponent +extends UpdatingComponent +implements OnInit{ canSignup = false isSignedup = false @@ -43,41 +46,28 @@ export class FrontcraftRaidComponent implements OnInit, OnDestroy{ tokens: {[itemname in string]: (Character & SRToken & Item)[]} = {} displayedtokens = {} search = "" - uuid constructor( private api: IApiService, private route: ActivatedRoute, private router: Router, private dialogService : NbDialogService, - private toast: NbToastrService + private toast: NbToastrService, + logger: LoggerService ){ - router.events.subscribe(event => { - if (event instanceof NavigationStart) { - this.ngOnDestroy() - } - }) + super(router, api, logger) } - ngOnDestroy = () => { - if(this.uuid) - this.api.get('PubSub').unsubscribe(this.uuid) - } - - async ngOnInit(){ - + ngOnInit(){ this.manageRaid = this.api.get('manageRaid') - const signupFeature = this.api.get('signup') + if(signupFeature){ this.canSignup = true } - await this.refresh() - - const res = await this.api.get('PubSub').subscribe(""+this.raid.id!, (data: RaidData) => { - this.display(data) + this.refresh().then(() => { + this.subscribe(String(this.raid.id), (data: RaidData) => this.display(data)) }) - this.uuid = res.uuid } itemSelect = async(item) => { @@ -143,7 +133,7 @@ export class FrontcraftRaidComponent implements OnInit, OnDestroy{ await signup.sign(auth.token.value, { ...this.mySignup, id: this.mySignup.characterid, - }, this.raid, value) + }, this.raid, value, this.mySignup.memo) this.toast.show('Signup', 'Success', { status: 'success' }) } @@ -163,9 +153,7 @@ export class FrontcraftRaidComponent implements OnInit, OnDestroy{ const raiddata = await raidManager.getRaidData({ id: param }) - this.display(raiddata) - } display = async (raiddata:RaidData) => { @@ -179,15 +167,17 @@ export class FrontcraftRaidComponent implements OnInit, OnDestroy{ }) 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){ - this.isSignedup = true this.mySignup = matchingSignup + this.isSignedup = true this.mySignup['status'] = matchingSignup['benched']?'Bench':matchingSignup['late']?'Late':'Attending' }else{ this.isSignedup = false this.mySignup = null } + this.changeSearch() } @@ -202,9 +192,7 @@ export class FrontcraftRaidComponent implements OnInit, OnDestroy{ }) if(filteredTokens.length > 0) this.displayedtokens[e[0]] = filteredTokens - }) - console.log(this.displayedtokens); - + }) } } diff --git a/src/frontend/src/app/frontcraft/pages/raids/createraid.compontent.ts b/src/frontend/src/app/frontcraft/pages/raids/createraid.compontent.ts index 2bf9e91..1107c6b 100644 --- a/src/frontend/src/app/frontcraft/pages/raids/createraid.compontent.ts +++ b/src/frontend/src/app/frontcraft/pages/raids/createraid.compontent.ts @@ -1,7 +1,6 @@ import { Component, OnInit } from '@angular/core'; -import { ClientApiService } from '../../services/client-login-api'; 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 { IApiService } from '../../services/ApiService'; @@ -31,7 +30,7 @@ export class FrontcraftCreateRaidsComponent implements OnInit { private api: IApiService ) {} - loadNext(cardData) { + loadNext() { } onTierSelect(){ diff --git a/src/frontend/src/app/frontcraft/pages/raids/raids.component.html b/src/frontend/src/app/frontcraft/pages/raids/raids.component.html index 80ddcda..30a8317 100644 --- a/src/frontend/src/app/frontcraft/pages/raids/raids.component.html +++ b/src/frontend/src/app/frontcraft/pages/raids/raids.component.html @@ -1,64 +1,70 @@ Upcoming raids - + - - -
- - -
-
- + Previous raids - + - {{raid.title}}
- {{raid.signupcount}} / {{raid.size}}
- {{raid.start | date : 'EEEE d MMMM @ HH:mm'}}
- {{raid.description}}
+ +
+ + + +

+ {{raid.title}} +

+ + + + + + +
+ +
+  {{raid.signupcount}} / {{raid.size}}
+
+  {{raid.start | date : 'HH:mm'}} + +  {{raid.start | date : 'EEE MMM d'}} +
+
+
-
- \ No newline at end of file + \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/pages/raids/raids.component.ts b/src/frontend/src/app/frontcraft/pages/raids/raids.component.ts index 802e01e..f98faa4 100644 --- a/src/frontend/src/app/frontcraft/pages/raids/raids.component.ts +++ b/src/frontend/src/app/frontcraft/pages/raids/raids.component.ts @@ -1,69 +1,59 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; -import { ClientApiService } from '../../services/client-login-api'; -import { NbWindowService, NbDialogService } from '@nebular/theme'; +import { NbDialogService } from '@nebular/theme'; import { FrontcraftCreateRaidsComponent } from './createraid.compontent'; import { Router, NavigationStart } from '@angular/router'; import { IApiService } from '../../services/ApiService'; +import { UpdatingComponent } from '../../UpdatingComponent'; +import { LoggerService } from '../../services/logger.service'; @Component({ selector: 'raids', templateUrl: 'raids.component.html', styleUrls: ['raids.component.scss'], }) -export class FrontcraftRaidsComponent implements OnInit, OnDestroy{ +export class FrontcraftRaidsComponent +extends UpdatingComponent +implements OnInit { manageRaid raids = [] oldraids = [] pageSize = 10; - uuid constructor( private api: IApiService, - private router: Router, - private dialogService: NbDialogService + router: Router, + private dialogService: NbDialogService, + logger: LoggerService ) { - this.manageRaid = this.api.get('manageRaid') - router.events.subscribe(event => { - if (event instanceof NavigationStart) { - this.ngOnDestroy() - } - }) + super(router, api, logger) } - ngOnDestroy = () => { - if(this.uuid) - this.api.get('PubSub').unsubscribe(this.uuid) + ngOnDestroy(){ + super.ngOnDestroy() } async ngOnInit() { - const res = await this.api.get('PubSub').subscribe("raids", () => { - this.refresh() - }) - this.uuid = res.uuid + this.subscribe("raids", () => this.refresh()) + + this.manageRaid = this.api.get('manageRaid') this.refresh() } - refresh() { - this.api.get('RaidManager').getRaids().then(raids => { - this.raids = raids - }) - + async refresh() { + this.raids = await this.api.get('RaidManager').getRaids() const raidManager = this.api.get('RaidManager') - raidManager.getPastRaids(10).then(raiddata => { - this.oldraids = raiddata - }) + this.oldraids = await raidManager.getPastRaids(10) } - create(){ - this.dialogService.open(FrontcraftCreateRaidsComponent, { + create() { + this.dialogService.open(FrontcraftCreateRaidsComponent, { closeOnBackdropClick: true, closeOnEsc: true, context: { templates: this.oldraids - } + } }) - } } diff --git a/src/frontend/src/app/frontcraft/pages/shop/buytoken.component.ts b/src/frontend/src/app/frontcraft/pages/shop/buytoken.component.ts index 33dfec9..6a4ef88 100644 --- a/src/frontend/src/app/frontcraft/pages/shop/buytoken.component.ts +++ b/src/frontend/src/app/frontcraft/pages/shop/buytoken.component.ts @@ -1,10 +1,8 @@ -import { Component, OnInit, ContentChild, AfterContentInit, ViewChild, AfterViewInit, Input, Output, EventEmitter } from '@angular/core'; -import { ClientApiService as ClientApiService } from '../../services/client-login-api'; -import { FrontcraftItemSelectComponent } from './itemselector.component'; -import { NbWindowService, NbWindowRef, NbToastrService, NbDialogService, NbDialogRef } from '@nebular/theme'; +import { Component, OnInit, Input } from '@angular/core'; +import { NbToastrService, NbDialogRef } from '@nebular/theme'; import { Item, Character, SRToken, Signup } from '../../../../../../backend/Types/Types'; 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'; @Component({ diff --git a/src/frontend/src/app/frontcraft/pages/shop/itemselector.component.ts b/src/frontend/src/app/frontcraft/pages/shop/itemselector.component.ts index ca5d986..95dc2b2 100644 --- a/src/frontend/src/app/frontcraft/pages/shop/itemselector.component.ts +++ b/src/frontend/src/app/frontcraft/pages/shop/itemselector.component.ts @@ -24,8 +24,16 @@ export class FrontcraftItemSelectComponent implements OnInit{ 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 => { this.allItems = items this.displayedItems = this.allItems diff --git a/src/frontend/src/app/frontcraft/pages/shop/shop.component.ts b/src/frontend/src/app/frontcraft/pages/shop/shop.component.ts index 9cb320d..3eac761 100644 --- a/src/frontend/src/app/frontcraft/pages/shop/shop.component.ts +++ b/src/frontend/src/app/frontcraft/pages/shop/shop.component.ts @@ -1,6 +1,4 @@ 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 { _Tiers, allItems } from '../../../../../../backend/Types/Items'; diff --git a/src/frontend/src/app/frontcraft/pages/user/user.component.html b/src/frontend/src/app/frontcraft/pages/user/user.component.html index 6eb4eb7..268d1f7 100644 --- a/src/frontend/src/app/frontcraft/pages/user/user.component.html +++ b/src/frontend/src/app/frontcraft/pages/user/user.component.html @@ -57,7 +57,7 @@ accent="info">
-

Avaiable Reserves

+

Available Reserves

{{user.MC}} MC | {{user.BWL}} BWL | {{user.ZG}} ZG | diff --git a/src/frontend/src/app/frontcraft/permissions/changePermissions/changePermissions.component.ts b/src/frontend/src/app/frontcraft/permissions/changePermissions/changePermissions.component.ts index 7a78365..5207ae4 100644 --- a/src/frontend/src/app/frontcraft/permissions/changePermissions/changePermissions.component.ts +++ b/src/frontend/src/app/frontcraft/permissions/changePermissions/changePermissions.component.ts @@ -1,6 +1,4 @@ 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 { NbToastrService } from '@nebular/theme'; import { IApiService } from '../../services/ApiService'; diff --git a/src/frontend/src/app/frontcraft/permissions/permissions-layout.component.ts b/src/frontend/src/app/frontcraft/permissions/permissions-layout.component.ts index 6726450..e414b08 100644 --- a/src/frontend/src/app/frontcraft/permissions/permissions-layout.component.ts +++ b/src/frontend/src/app/frontcraft/permissions/permissions-layout.component.ts @@ -1,5 +1,4 @@ import { Component, OnInit } from '@angular/core'; -import { ClientApiService } from '../services/client-login-api'; import { Router } from '@angular/router'; @Component({ diff --git a/src/frontend/src/app/frontcraft/services/ApiService.ts b/src/frontend/src/app/frontcraft/services/ApiService.ts index ee8875a..a56ad06 100644 --- a/src/frontend/src/app/frontcraft/services/ApiService.ts +++ b/src/frontend/src/app/frontcraft/services/ApiService.ts @@ -1,23 +1,30 @@ import { Auth, FrontcraftIfc, FrontcraftFeatureIfc, User, SomeOf } from '../../../../../backend/Types/Types'; import { saltedHash } from '../../../../../backend/Util/hash'; -import { RPCSocket } from 'rpclibrary'; +import { RPCSocket, ConnectedSocket } from 'rpclibrary'; export class IApiService{ - getUnprivilegedSocket: () => RPCSocket & FrontcraftIfc + getUnprivilegedSocket: () => ConnectedSocket getAuth: () => Auth | undefined checkLogin: () => Promise initialize: () => Promise - getCurrentUser: () => User | undefined + getCurrentUser: () => User login: (username: string, password: string) => Promise> connectShoutbox: (callback:Function) => Promise kick: () => Promise logout: () => Promise - get: (feature : K) => K extends keyof FrontcraftIfc?FrontcraftIfc[K]: - K extends keyof FrontcraftFeatureIfc?(FrontcraftFeatureIfc[K] | void): - never + get: (feature : K) => K extends keyof FrontcraftIfc + ? FrontcraftIfc[K] + : K extends keyof FrontcraftFeatureIfc + ? (FrontcraftFeatureIfc[K] | void) + : never } export async function hash(value:string) : Promise{ return saltedHash(value, "") } +export const GuestUser: User = { + username: 'Guest', + pwhash: '', + rank: 'Guest' +} \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/services/client-login-api.ts b/src/frontend/src/app/frontcraft/services/client-login-api.ts index a5edbbb..def1b23 100644 --- a/src/frontend/src/app/frontcraft/services/client-login-api.ts +++ b/src/frontend/src/app/frontcraft/services/client-login-api.ts @@ -1,24 +1,28 @@ 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 { 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 @Injectable() export class ClientApiService implements IApiService{ - private socket:RPCSocket & FrontcraftIfc; + private socket:ConnectedSocket private auth:Auth - private privSocket: RPCSocket & SomeOf + private privSocket: RPCSocket> - constructor(){ + constructor( + private logger: LoggerService + ){ window['s'] = this } - getUnprivilegedSocket = () : RPCSocket & FrontcraftIfc => this.socket + getUnprivilegedSocket = () : ConnectedSocket => this.socket - private getPrivilegedSocket = async (auth:Auth) : Promise> => { + private getPrivilegedSocket = async (auth:Auth) : Promise>> => { if(this.privSocket) { return this.privSocket } @@ -38,17 +42,17 @@ export class ClientApiService implements IApiService{ sock.hook('getUserData', () => auth) sock.on('close', async () => { - console.log("priv#close"); + this.logger.log("priv#close"); //handled via unprivileged socket }) sock.on('error', async (e) => { - console.log("priv#error", e); + this.logger.log("priv#error", e); //handled via unprivileged socket }) - const authSock = await sock.connect>(auth.token.value) + const authSock = await sock.connect(auth.token.value) this.auth = auth this.privSocket = authSock @@ -74,8 +78,8 @@ export class ClientApiService implements IApiService{ } } - getCurrentUser = () : User | undefined => { - return this.auth?this.auth.user:undefined + getCurrentUser = () : User => { + return this.auth?this.auth.user:GuestUser } login = async (username: string, password: string) : Promise> => { @@ -102,8 +106,8 @@ export class ClientApiService implements IApiService{ } async connectShoutbox(callback:Function) : Promise{ - const res = await this.get('Shoutbox').subscribe(callback) - return async (msg: ShoutMessage) => await this.get('Shoutbox').shout(res.uuid, msg) + const uuid = await this.get('Shoutbox').subscribe(callback) + return async (msg: ShoutMessage) => await this.get('Shoutbox').shout(uuid, msg) } kick = async () => { @@ -113,6 +117,7 @@ export class ClientApiService implements IApiService{ logout = async () => { Cookies.remove('token') + if(this.privSocket) this.privSocket.unhook('kick') if(this.auth){ try{ await this.socket.UserManager.logout(this.auth.user.username, this.auth.token.value) @@ -133,11 +138,11 @@ export class ClientApiService implements IApiService{ } try{ - let conn = new RPCSocket(20000, window.location.hostname) + let conn = new RPCSocket(20000, window.location.hostname) conn.on('close', async () => { }) - this.socket = await conn.connect() + this.socket = await conn.connect() }catch(e){ alert('Unable to connect. The server appears to be down.\nPress OK to refresh the page') location.reload() diff --git a/src/frontend/src/app/frontcraft/services/logger.service.ts b/src/frontend/src/app/frontcraft/services/logger.service.ts new file mode 100644 index 0000000..beab1a1 --- /dev/null +++ b/src/frontend/src/app/frontcraft/services/logger.service.ts @@ -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) + } + } +} \ No newline at end of file diff --git a/src/frontend/src/app/frontcraft/services/server-login-api.ts b/src/frontend/src/app/frontcraft/services/server-login-api.ts index 9916e66..84b579b 100644 --- a/src/frontend/src/app/frontcraft/services/server-login-api.ts +++ b/src/frontend/src/app/frontcraft/services/server-login-api.ts @@ -1,19 +1,23 @@ import { Injectable } from "@angular/core"; import {RPCSocket} from 'rpclibrary/js/src/Frontend' 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 @Injectable() export class ServerApiService implements IApiService{ - private socket:RPCSocket & FrontcraftIfc; + private socket:ConnectedSocket private auth:Auth private privSocket: RPCSocket & SomeOf - constructor(){} + constructor( + private logger: LoggerService + ){} - getUnprivilegedSocket = () : RPCSocket & FrontcraftIfc => this.socket + getUnprivilegedSocket = () : ConnectedSocket => this.socket get = (feature : K) : K extends keyof FrontcraftIfc?FrontcraftIfc[K]: K extends keyof FrontcraftFeatureIfc?(FrontcraftFeatureIfc[K] | void): @@ -22,8 +26,8 @@ export class ServerApiService implements IApiService{ return this.socket[feature] } - getCurrentUser = () : User | undefined => { - return this.auth?this.auth.user:undefined + getCurrentUser = () : User => { + return GuestUser } login = async (username: string, password: string) : Promise> => { @@ -42,13 +46,13 @@ export class ServerApiService implements IApiService{ //attach error handler now so failed logins dont trigger reloads this.socket.on('error', async (err) => { - console.log(err); + this.logger.log(err); }) } async connectShoutbox(callback:Function) : Promise{ - return console.log + return this.logger.log } kick = async () => { @@ -60,7 +64,7 @@ export class ServerApiService implements IApiService{ try{ await this.socket.UserManager.logout(this.auth.user.username, this.auth.token.value) }catch(e){ - console.warn(e); + this.logger.warn(e); } } @@ -76,13 +80,13 @@ export class ServerApiService implements IApiService{ } try{ - let conn = new RPCSocket(20000, window.location.hostname) + let conn = new RPCSocket(20000, window.location.hostname) conn.on('close', async () => { }) - this.socket = await conn.connect() + this.socket = await conn.connect() }catch(e){ - console.log(e); + this.logger.log(e); throw new Error("Websocket cannot connect") } } diff --git a/src/frontend/src/main.server.ts b/src/frontend/src/main.server.ts index e429066..d33b2b9 100644 --- a/src/frontend/src/main.server.ts +++ b/src/frontend/src/main.server.ts @@ -6,4 +6,4 @@ export { AppServerModule } from './app/app.server.module'; import { IApiService } from './app/frontcraft/services/ApiService'; export { IApiService } export { ServerApiService } from './app/frontcraft/services/server-login-api'; - +export { LoggerService } from './app/frontcraft/services/logger.service' \ No newline at end of file diff --git a/src/frontend/src/main.ts b/src/frontend/src/main.ts index c8b31f0..700a2a9 100644 --- a/src/frontend/src/main.ts +++ b/src/frontend/src/main.ts @@ -10,18 +10,23 @@ import { FrontcraftAppModule } from './app/app.module'; import { environment } from './environments/environment'; import { IApiService } from "./app/frontcraft/services/ApiService" import { ClientApiService } from './app/frontcraft/services/client-login-api'; +import { LoggerService } from './app/frontcraft/services/logger.service'; if (environment.production) { enableProdMode(); } - -const serviceObj = new ClientApiService() +const loggerService = new LoggerService() +const serviceObj = new ClientApiService(loggerService) serviceObj.initialize().then(_ => { platformBrowserDynamic([ { provide: IApiService, deps: [], useValue: serviceObj + },{ + provide: LoggerService, + deps: [], + useValue: loggerService } ]).bootstrapModule(FrontcraftAppModule) .catch(err => console.error(err)); diff --git a/src/frontend/tsconfig.app.json b/src/frontend/tsconfig.app.json index 4dd64eb..4537c50 100644 --- a/src/frontend/tsconfig.app.json +++ b/src/frontend/tsconfig.app.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { + "strict": false, "outDir": "./out-tsc/app", "paths": { "@angular/*": [ diff --git a/test/backendTest.ts b/test/backendTest.ts index 829306d..6c9d605 100644 --- a/test/backendTest.ts +++ b/test/backendTest.ts @@ -1,8 +1,8 @@ import { Injector } from "../src/backend/Injector/Injector"; 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 { SpecT } from "../src/backend/Types/PlayerSpecs"; @@ -120,8 +120,8 @@ describe('Frontcraft', () => { let auth: Auth, adminUser: User, server: FrontworkAdmin, - client: RPCSocket & FrontcraftIfc, - adminClient: RPCSocket & FrontcraftFeatureIfc, + client: ConnectedSocket, + adminClient: ConnectedSocket, raids: Raid[] = [], users: { [username in string]: { account: User, character: Character, auth: Auth, signup?: Signup, item?: string } } = {} @@ -168,12 +168,12 @@ describe('Frontcraft', () => { }).then(adminUser => { client.UserManager.login(adminUser.username, 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb').then(auth => { - const sock = new RPCSocket( + const sock = new RPCSocket( auth.port, "localhost" ) - sock.connect(auth.token.value).then(cli => { + sock.connect(auth.token.value).then(cli => { adminClient = cli sock.hook('kick', () => { console.log("I got kicked"); @@ -240,7 +240,7 @@ describe('Frontcraft', () => { it('should sign up', (done) => { 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 => { adminClient.signup.getSignups(raids[0]).then(s => { if (s.length == testAccounts.length) {