checkpoint

This commit is contained in:
peter
2020-03-08 14:16:06 +01:00
parent c4ca96fef9
commit b2707bb88f
22 changed files with 5904 additions and 509 deletions
+25
View File
@@ -0,0 +1,25 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"frontcraft": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src/backend",
"prefix": "app",
"architect": {
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/server",
"main": "src/backend/Launcher.ts",
"tsConfig": "tsconfig.server.json"
}
}
}
}
},
"defaultProject": "frontcraft"
}
+5581 -388
View File
File diff suppressed because it is too large Load Diff
+13 -4
View File
@@ -1,6 +1,6 @@
{
"name": "frontblock-admin",
"description": "Dynamic configurator for frontblock",
"name": "frontcraft",
"description": "Guild website framework",
"version": "1.0.0",
"scripts": {
"tsc": "tsc",
@@ -9,13 +9,14 @@
"start-backend": "npm run build-backend && npm run launch",
"test": "rm -rf data && npm run build-backend && mocha lib/test/backendTest.js",
"build": "npm run build-backend && npm run build-frontend",
"build-backend": "npm run clean-backend && tsc",
"build-backend": "npm run clean-backend && ng v && ng run frontcraft:server",
"build-frontend": "npm run clean-frontend && (mkdir static || rm -rf static/*) && npm run build-dashboard",
"build-dashboard": "cd src/frontend && npm i && npm run build && cp -r dist/* ../../static",
"clean": "rm -rf data && npm run clean-backend && npm run clean-frontend",
"clean-backend": "rm -rf lib plugins config widget .rpt2_cache *.js *.ts",
"clean-frontend": "rm -rf src/frontend/dist static",
"webpack": "webpack --config src/backend/webpack.prod.js --progress --colors"
"webpack": "webpack --config src/backend/webpack.prod.js --progress --colors",
"setangular": "ng update @angular/cli@8.2.2 @angular/core@8.2.2 @angular/compiler-cli@8.2.2"
},
"repository": {
"type": "git",
@@ -24,7 +25,14 @@
"author": "frontblock.me",
"license": "ISC",
"dependencies": {
"@angular-devkit/build-angular": "^0.900.5",
"@angular/compiler": "^9.0.5",
"@angular/compiler-cli": "^9.0.5",
"@angular/platform-server": "^9.0.5",
"@nguniversal/express-engine": "^9.0.1",
"@nguniversal/module-map-ngfactory-loader": "^8.2.6",
"@types/mocha": "^5.2.7",
"angular": "^1.7.9",
"bsert": "0.0.10",
"bsock": "^0.1.9",
"child-process-promise": "^2.2.1",
@@ -56,6 +64,7 @@
"xml2js": "^0.4.22"
},
"devDependencies": {
"@angular/cli": "^9.0.5",
"@types/express": "^4.17.0",
"@types/node": "^11.13.19",
"@types/semver": "^6.0.1",
+22
View File
@@ -22,6 +22,10 @@ import { Injector } from '../Injector/Injector';
import { Shoutbox } from '../Components/Shoutbox/Shoutbox';
import { PubSub } from '../Components/PubSub/PubSub';
import { ngExpressEngine } from '@nguniversal/express-engine';
import { APP_BASE_HREF } from '@angular/common';
import { AppServerModule } from './app.server.module';
const logger = getLogger("admin", 'debug')
@@ -132,6 +136,24 @@ implements TableDefinitionExporter, IAdmin {
this.express = express()
this.express.use('/', express.static('static'))
this.express.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
this.express.set('view engine', 'html');
this.express.set('views', 'static');
// Serve static files from /browser
this.express.get('*.*', express.static('static', {
maxAge: '1y'
}));
// All regular routes use the Universal engine
this.express.get('*', (req, res) => {
res.render('index', { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
/**
* get the compiled FrontendPlugins.js
*/
+19
View File
@@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
import { AppModule } from '../../frontend/src/app/app.module';
import { AppComponent } from '../../frontend/src/app/app.component';
@NgModule({
imports: [
AppModule,
ServerModule,
ModuleMapLoaderModule
],
providers: [
// Add universal-only providers here
],
bootstrap: [ AppComponent ],
})
export class AppServerModule {}
+2 -6
View File
@@ -2,15 +2,11 @@ import { ConfigLoader } from "loadson";
import { Inject, Injectable } from "../../Injector/ServiceDecorator";
import { GuildManagerFeatureIfc, GuildManagerIfc } from "./RPCInterface";
import { FrontworkComponent } from "../../Types/FrontworkComponent";
import { _Rank, Rank } from "../../Types/Types";
import { _Rank, Rank, Guild } from "../../Types/Types";
import { IAdmin } from "../../Admin/Interface";
import { IGuildManager } from "./Interface";
export type Guild = {
name: string
realm: string
description: string
};
@Injectable(IGuildManager)
export class GuildManager
+1 -2
View File
@@ -1,5 +1,4 @@
import { Rank } from "../../Types/Types"
import { Guild } from "./GuildManager"
import { Rank, Guild } from "../../Types/Types"
export class IGuildManager{
getHeadCount: () => Promise<{rank:Rank, count: number}[]>
+2 -1
View File
@@ -3,7 +3,8 @@ import { Tiers } from "../../Types/Items"
export class IItemManager{
getItems: () => Promise<Item[]>
fetchItem: (name:string) => Promise<Item>
getItem: (name:string, tier?:Tiers) => Promise<Item | undefined>
fetchItem: (name:string, tier?:Tiers) => Promise<Item | undefined>
buyToken: (usertoken: string, charactername:string, itemname:string, signup:Signup) => Promise<(SRToken & Character & Item) | undefined>
setPriority: (itemname:string, priority: any) => Promise<void>
calculatePriorities: (itemname: string, character:Character) => Promise<number>
+95 -26
View File
@@ -3,14 +3,14 @@ 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 } from "../../Types/Types";
import { TableDefiniton, Item, User, Character, SRToken, SRPriority, Spec, Signup, Raid, Stats } from "../../Types/Types";
import { IAdmin } from "../../Admin/Interface";
import { IItemManager } from "./Interface";
import { getLogger } from "log4js";
import { IUserManager } from "../User/Interface";
import { ICharacterManager } from "../Character/Interface";
import { IPubSub } from "../PubSub/Interface";
import { IRaidManager } from "../Raid/Interface";
import { getLogger } from "frontblock-generic/Types";
const fetch = require('node-fetch')
const xml2js = require('xml2js');
@@ -60,13 +60,13 @@ export class ItemManager
}]
notifyRaid = async (raid: Raid | { id: number }) => {
const data = await this.raidManager.getRaidData(<Raid>raid)
this.pubsub.publish("" + raid.id, data)
await this.notifyRaids()
const data = await this.raidManager.getRaidData(<Raid>raid)
await this.pubsub.publish("" + raid.id, data)
}
notifyRaids = async () => {
this.pubsub.publish('raids', undefined)
await this.pubsub.publish('raids', undefined)
}
wipeCurrencyAndItems = async () => {
@@ -78,26 +78,58 @@ export class ItemManager
getItems = async (): Promise<Item[]> => await this.admin.knex.select('*').from('items')
getItem = async (name: string): Promise<Item> => {
return await this.admin.knex('items').select('*').where('itemname', '=', name).first()
getItem = async (name: string, tier?:Tiers): Promise<Item | undefined> => {
let item = await this.admin.knex('items').select('*').where('itemname', '=', name).first()
if(!item){
item = await this.fetchItem(name, tier)
if (!item)
return
await this.admin
.knex('items')
.insert({
...item,
stats: JSON.stringify(item.stats)
})
return this.getItem(name, tier)
}
fetchItem = async (name: string): Promise<Item> => {
item.stats = JSON.parse(item.stats)
return item
}
fetchItem = async (name: string, tier?:Tiers): Promise<Item | undefined> => {
const res = await fetch('https://classic.wowhead.com/item=' + name + '&xml');
const txt = await res.text();
const r = await parser.parseStringPromise(txt);
try{
if(!r.wowhead.item || !r.wowhead.item[0])
return
try {
return <Item>{
const j = JSON.parse('{'+r.wowhead.item[0].json[0]+"}")
if(!tier){
if(j.sourcemore && Number.isInteger(j.sourcemore[0].t) && j.sourcemore[0].t > 0){
tier = _Tiers[j.sourcemore[0].t-1]
}else{
tier = "MC"
}
}
const stats = JSON.parse('{'+r.wowhead.item[0].jsonEquip[0]+"}")
const item = <Item>{
itemname: r.wowhead.item[0].name[0],
iconname: r.wowhead.item[0].icon[0]._,
url: r.wowhead.item[0].link[0],
quality: r.wowhead.item[0].quality[0]._,
tooltip: r.wowhead.item[0].htmlTooltip[0]
tooltip: r.wowhead.item[0].htmlTooltip[0],
stats: toStats(stats),
tier: tier
}
} catch (e) {
console.log(name)
throw e
return item
}catch(e){
getLogger('ItemManager', 'error').error(name, e);
}
}
@@ -139,6 +171,7 @@ export class ItemManager
table.boolean('hidden').defaultTo(false).notNullable()
table.string('tooltip').notNullable()
table.enu('tier', _Tiers).notNullable()
table.json('stats').notNullable()
}
}]
}
@@ -249,7 +282,7 @@ export class ItemManager
await this.admin
.knex('priorities')
.insert(<SRPriority>{
itemname: item.itemname,
itemname: item!.itemname,
...priority
})
}
@@ -354,19 +387,55 @@ export class ItemManager
if (countCache != Object.values(itemTiers).flat().length) {
await Promise.all(
Object.entries(itemTiers)
.map((kv) => Promise.all(
kv[1].map(i => this.fetchItem(i)
.then(item =>
this.admin
.knex('items')
.insert({
tier: kv[0],
...item
}).catch(() => { })
)
)
.map((kv: [string, string[]]) => Promise.all(
kv[1].map(i => this.getItem(i, kv[0] as Tiers) )
))
)
}
}
}
function toStats(obj:any){
if(!obj) obj = {}
return {
stamina: obj.sta || 0,
agility: obj.agi || 0,
strength: obj.str || 0,
intellect: obj.int || 0,
spirit: obj.spi || 0,
attackpower: obj.atkpwr || 0,
meleehit: obj.mlehitpct || 0,
meleecrit: obj.mlecritstrkpct || 0,
rangeattackpower: obj.rgdatkpwr || 0,
rangehit: obj.rgdhitpct || 0,
rangecrit: obj.rgdcritstrkpct || 0,
spellpower: obj.splpwr || 0,
spellhit: obj.splhitpct || 0,
spellcrit: obj.splcritstrkpct || 0,
heal: obj.splheal || 0,
frostspellpower: obj.frosplpwr || 0,
firespellpower: obj.firsplpwr || 0,
naturespellpower: obj.natsplpwr || 0,
shadowspellpower: obj.shasplpwr || 0,
arcanespellpower: obj.arcsplpwr || 0,
fireresist: obj.firres || 0,
arcaneresist: obj.arcres || 0,
frostresist: obj.frores || 0,
natureresist: obj.natres || 0,
shadowresist: obj.shares || 0,
armor: obj.armor || 0,
defense: obj.def || 0,
block: obj.blockpct || 0,
blockvalue: obj.blockamount || 0,
dodge: obj.dodgepct || 0,
parry: obj.parrypct || 0,
} as Stats
}
+1 -1
View File
@@ -2,7 +2,7 @@ import { IItemManager } from "./Interface"
export type ItemManagerIfc = {
ItemManager: {
getItem: IItemManager['fetchItem']
getItem: IItemManager['getItem']
getItems: IItemManager['getItems']
buyToken: IItemManager['buyToken']
calculatePriorities: IItemManager['calculatePriorities']
-4
View File
@@ -70,7 +70,6 @@ export const T1 = [
"Cenarion Bracers",
"Cenarion Vestments",
"Cenarion Helm",
"Cenarion Helm",
"Cenarion Leggings",
"Cenarion Spaulders",
"Giantstalker's Belt",
@@ -106,7 +105,6 @@ export const T1 = [
"Robes of Prophecy",
"Vambraces of Prophecy",
"Nightslayer Belt",
"Nightslayer Belt",
"Nightslayer Bracelets",
"Nightslayer Chestpiece",
"Nightslayer Cover",
@@ -120,7 +118,6 @@ export const T1 = [
"Felheart Robes",
"Felheart Shoulder Pads",
"Felheart Horns",
"Felheart Horns",
"Belt of Might",
"Bracers of Might",
"Breastplate of Might",
@@ -150,7 +147,6 @@ export const T1 = [
"Bloodfang Hood",
"Bloodfang Pants",
"Helmet of Ten Storms",
"Helmet of Ten Storms",
"Nemesis Skullcap",
"Nemesis Leggings",
"Helm of Wrath",
+48
View File
@@ -81,6 +81,53 @@ export type SRPriority = {
modifier:number
}
export type Stats = {
stamina: number,
agility: number,
strength: number,
intellect: number,
spirit: number,
fireresist: number,
frostresist: number,
arcaneresist: number,
shadowresist: number,
natureresist: number,
spellpower: number,
spellhit: number,
spellcrit: number,
heal: number,
frostspellpower: number,
firespellpower: number,
naturespellpower: number,
shadowspellpower: number,
arcanespellpower: number,
attackpower: number,
meleehit: number,
meleecrit: number,
rangeattackpower: number,
rangehit: number,
rangecrit: number,
armor:number,
defense: number,
block: number,
blockvalue: number,
parry: number,
dodge:number
}
export type Guild = {
name: string
realm: string
description: string
};
export type Item = {
itemname:string
iconname:string
@@ -89,6 +136,7 @@ export type Item = {
hidden:boolean
tooltip: string
tier: Tiers
stats: Stats
}
export type User = {
+1 -1
View File
@@ -13,7 +13,7 @@
"ng": "ng",
"conventional-changelog": "conventional-changelog",
"start": "ng serve",
"build": "ng build",
"build": "ng build --aot",
"build:prod": "npm run build -- --prod --aot",
"test": "ng test",
"test:coverage": "rimraf coverage && npm run test -- --code-coverage",
@@ -31,8 +31,7 @@
<nb-action class="user-action">
<nb-user [nbContextMenu]="userMenu"
[onlyPicture]="false"
[name]="user?.username"
[picture]="user?.picture">
[name]="user?.username">
</nb-user>
</nb-action>
@@ -29,6 +29,7 @@ export class RegisterComponent implements OnInit{
selectedSpec = specs[this.selectedClass][0]
specs = specs[this.selectedClass]
showApplication = false
submitted = false
constructor(
private router : Router,
@@ -1,90 +1,106 @@
import { Component, OnInit } from '@angular/core';
import { Item } from '../../../../../../backend/Types/Types';
import { Item, Stats } from '../../../../../../backend/Types/Types';
import { NbToastrService } from '@nebular/theme';
import { ApiService } from '../../services/login-api';
@Component({
selector: 'armory',
templateUrl: './armory.component.html',
})
export class FrontcraftArmoryComponent implements OnInit{
export class FrontcraftArmoryComponent implements OnInit {
servers = servers
submitted = false
charactername = ""
server = "Gandling"
region = "EU"
gear:string[] = []
gear: Item[] = []
constructor(
private api: ApiService,
private toastr: NbToastrService
){}
) { }
async submit(){
const dataLink = "https://classic.warcraftlogs.com:443/v1/parses/character/"+this.charactername.toLowerCase().replace(/^\w/, c => c.toUpperCase())+"/"+this.server+"/"+this.region+"?api_key=c698515ab4f592cdb848d80b3abe616c&metric=dps"
async submit() {
const dataLink = "https://classic.warcraftlogs.com:443/v1/parses/character/" + this.charactername.toLowerCase().replace(/^\w/, c => c.toUpperCase()) + "/" + this.server + "/" + this.region + "?api_key=c698515ab4f592cdb848d80b3abe616c&metric=dps"
fetch(dataLink).then(raw => raw.json().then((json:any[]) => {
if(json.length < 1) return
fetch(dataLink).then(raw => raw.json().then(async (json: any[]) => {
if (json.length < 1) return
let min = -1
let data
for(let i = json.length-1; i > 0; i--){
if(json[i].startTime > min && json[i].gear[0].name != "Unknown Item"){
for (let i = json.length - 1; i > 0; i--) {
if (json[i].startTime > min && json[i].gear[0].name != "Unknown Item") {
data = json[i]
break
}
}
if(!data){
if (!data) {
this.toastr.danger('Unknown character, or no warcraftlogs', "Error", {
duration: 2000
})
return
}
this.gear = data.gear.map(item => {return <Item>{
itemname: item.name,
iconname: item.icon.replace('.jpg', ''),
quality: item.quality.replace(/^\w/, c => c.toUpperCase()),
url: "https://classic.wowhead.com/item="+item.id,
}}).filter(item => item.itemname != "Unknown Item")
const maybeItems: (Item | undefined)[] = await Promise.all(data.gear
.filter(item => item.name != "Unknown Item")
.map(async item => await this.api.get('ItemManager').getItem(item.name)))
const geardata = maybeItems.filter(maybeItem => maybeItem != null)
this.gear = geardata
console.log(this.gear.map(g => g.stats).reduce(sumStats))
}))
}
async ngOnInit(){
async ngOnInit() {
}
}
function sumStats(stat1: Stats, stat2: Stats):Stats{
const ret = {} as any
Object.keys(stat1).forEach(key => {
ret[key] = stat1[key] + stat2[key]
})
return ret
}
const servers = [
"Ashbringer"
,"Bloodfang"
,"Dreadmist"
,"Firemaw"
,"Flamelash"
,"Gandling"
,"Gehennas"
,"Golemagg"
,"Hydraxian Waterlords"
,"Judgement"
,"Mirage Rach"
,"Mograine"
,"Nethergarde"
,"Noggenfogg"
,"Pyrewood Village"
,"Razorgore"
,"Shazzrah"
,"Skullflame"
,"Stonespine"
,"Ten Storms"
,"Zandalar Tribe"
,"Amnennar"
,"Auberdine"
,"Finkle"
,"Sulfuron"
,"Everlook"
,"Lucifron"
,"Razorfen"
,"Patchwerk"
,"Venoxis"
,"Dragons Call"
,"Lakeshire"
,"Transcendence"
, "Bloodfang"
, "Dreadmist"
, "Firemaw"
, "Flamelash"
, "Gandling"
, "Gehennas"
, "Golemagg"
, "Hydraxian Waterlords"
, "Judgement"
, "Mirage Rach"
, "Mograine"
, "Nethergarde"
, "Noggenfogg"
, "Pyrewood Village"
, "Razorgore"
, "Shazzrah"
, "Skullflame"
, "Stonespine"
, "Ten Storms"
, "Zandalar Tribe"
, "Amnennar"
, "Auberdine"
, "Finkle"
, "Sulfuron"
, "Everlook"
, "Lucifron"
, "Razorfen"
, "Patchwerk"
, "Venoxis"
, "Dragons Call"
, "Lakeshire"
, "Transcendence"
]
@@ -24,7 +24,7 @@ export class FrontcraftCharacterComponent implements OnInit{
boss1 = {}
gear1:string[] = []
boss2= {}
boss2= {name: undefined, date:undefined}
gear2:string[] = []
constructor(
@@ -10,6 +10,7 @@ import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
})
export class FrontcraftArchiveComponent implements OnInit{
manageRaid = false
canSignup = false
isSignedup = false
canManage = false
@@ -1,6 +1,6 @@
<div class="row">
<div class="col-12 col-xl-6">
<nb-card [size]="giant">
<nb-card [size]="'giant'">
<nb-card-body>
<nb-tabset>
<nb-tab tabTitle="Info">
+4 -16
View File
@@ -3,7 +3,7 @@ import { FrontworkAdmin } from "../src/backend/Admin/Admin";
import { T1, T2, Tiers } from "../src/backend/Types/Items";
import { RPCSocket } from "rpclibrary";
import { FrontcraftIfc, Auth, User, FrontcraftFeatureIfc, Raid, Character, Rank, Class, Race, SRPriority, 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";
@@ -129,18 +129,6 @@ describe('Frontcraft', () => {
return client.UserManager.createUser(user)
}
const login = async (name: string) : Promise<Auth> => {
const auth = await client.UserManager.login(name, 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb')
if(users[name]) users[name] = {
...users[name],
auth: auth
}
return auth
}
const createAccountAndUser = async (acc: protoAccount) => {
const account = await createAccount({
pwhash: 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb', //sha256("a")
@@ -573,8 +561,8 @@ describe('Frontcraft', () => {
const user = Object.values(users)[0]
const itemname = "Maladath, Runed Blade of the Black Flight"
client.ItemManager.getItem(itemname).then(async item => {
await adminClient.softreserveCurrency.incrementCurrency(user.account, item.tier, 2)
const before = await client.ItemManager.getToken(user.character, item)
await adminClient.softreserveCurrency.incrementCurrency(user.account, item!.tier, 2)
const before = await client.ItemManager.getToken(user.character, item!)
if (!before || before.level !== 4) {
console.log("expected level to be 4", before ? before.level : '?');
@@ -690,7 +678,7 @@ describe('Frontcraft', () => {
let streaks = await client.ItemManager.getTokens(user.character, [BWL0.tier], false)
if (reserves!.length != 1
|| streaks!.length != 0
|| reserves![0].itemname !== T2_0.itemname
|| reserves![0].itemname !== T2_0!.itemname
|| reserves![0].level !== 1) {
console.log("Bad Token status 1", reserves, streaks);
done(new Error("Bad Token status"))
+3 -4
View File
@@ -3,13 +3,12 @@
"strictPropertyInitialization": false,
"noImplicitAny": false,
"target": "ESnext",
"module": "commonjs",
"declaration": true,
"outDir": "./lib",
"strict": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": ["src/backend/**/*", "test/**/*"],
"exclude": ["node_modules", "**/__tests__/*"]
"angularCompilerOptions": {
"entryModule": "./src/backend/Admin/app.server.module#AppServerModule"
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./lib",
"module": "commonjs",
"types": ["node"]
},
"include": ["src/backend/**/*", "test/**/*"],
"exclude": ["node_modules", "**/__tests__/*"],
"angularCompilerOptions": {
"entryModule": "./src/app/app.server.module#AppServerModule"
}
}