Browse Source

work

master
peter 5 years ago
parent
commit
5cf7a25e77
75 changed files with 2244 additions and 758 deletions
  1. 38
    19
      package-lock.json
  2. 4
    2
      package.json
  3. 11
    5
      src/backend/Admin/Admin.ts
  4. 1
    1
      src/backend/Admin/Interface.ts
  5. 45
    34
      src/backend/Components/Character/CharacterManager.ts
  6. 4
    2
      src/backend/Components/Character/Interface.ts
  7. 4
    3
      src/backend/Components/Character/RPCInterface.ts
  8. 0
    34
      src/backend/Components/Debugger/Debugger.ts
  9. 12
    20
      src/backend/Components/Guild/GuildManager.ts
  10. 10
    2
      src/backend/Components/Item/Interface.ts
  11. 183
    22
      src/backend/Components/Item/ItemManager.ts
  12. 13
    1
      src/backend/Components/Item/RPCInterface.ts
  13. 0
    18
      src/backend/Components/Login/RPCInterface.ts
  14. 8
    21
      src/backend/Components/RPCConfigLoader.ts
  15. 9
    2
      src/backend/Components/Raid/Interface.ts
  16. 7
    0
      src/backend/Components/Raid/RPCInterface.ts
  17. 237
    39
      src/backend/Components/Raid/RaidManager.ts
  18. 14
    0
      src/backend/Components/Shoutbox/Interface.ts
  19. 13
    0
      src/backend/Components/Shoutbox/RPCInterface.ts
  20. 64
    0
      src/backend/Components/Shoutbox/Shoutbox.ts
  21. 9
    1
      src/backend/Components/User/Interface.ts
  22. 26
    0
      src/backend/Components/User/RPCInterface.ts
  23. 140
    65
      src/backend/Components/User/UserManager.ts
  24. 1
    3
      src/backend/Injector/Injector.ts
  25. 6
    6
      src/backend/Injector/ServiceDecorator.ts
  26. 46
    7
      src/backend/Types/Types.ts
  27. 7
    0
      src/backend/Util/hash.ts
  28. 3
    3
      src/frontend/src/app/@core/core.module.ts
  29. 43
    0
      src/frontend/src/app/@theme/components/header/chat.component.ts
  30. 15
    4
      src/frontend/src/app/@theme/components/header/header.component.html
  31. 48
    22
      src/frontend/src/app/@theme/components/header/header.component.ts
  32. 12
    1
      src/frontend/src/app/@theme/theme.module.ts
  33. 2
    2
      src/frontend/src/app/app.component.ts
  34. 10
    2
      src/frontend/src/app/app.module.ts
  35. 2
    2
      src/frontend/src/app/frontcraft/auth/auth-layout.component.ts
  36. 1
    1
      src/frontend/src/app/frontcraft/auth/login/login.component.html
  37. 2
    2
      src/frontend/src/app/frontcraft/auth/login/login.component.ts
  38. 3
    3
      src/frontend/src/app/frontcraft/auth/logout/logout.component.ts
  39. 0
    15
      src/frontend/src/app/frontcraft/auth/register/register.component.html
  40. 15
    15
      src/frontend/src/app/frontcraft/auth/register/register.component.ts
  41. 13
    0
      src/frontend/src/app/frontcraft/pages/character/character.component.html
  42. 7
    5
      src/frontend/src/app/frontcraft/pages/character/character.component.ts
  43. 34
    0
      src/frontend/src/app/frontcraft/pages/characters/characters.component.html
  44. 123
    0
      src/frontend/src/app/frontcraft/pages/characters/characters.component.ts
  45. 0
    35
      src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.html
  46. 0
    42
      src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.scss
  47. 0
    63
      src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.ts
  48. 0
    10
      src/frontend/src/app/frontcraft/pages/dashboard/tree-grid-shared.scss
  49. 18
    18
      src/frontend/src/app/frontcraft/pages/pages-layout.component.ts
  50. 29
    9
      src/frontend/src/app/frontcraft/pages/pages-routing.module.ts
  51. 44
    7
      src/frontend/src/app/frontcraft/pages/pages.module.ts
  52. 0
    36
      src/frontend/src/app/frontcraft/pages/people/people.component.html
  53. 0
    42
      src/frontend/src/app/frontcraft/pages/people/people.component.scss
  54. 0
    64
      src/frontend/src/app/frontcraft/pages/people/people.component.ts
  55. 0
    10
      src/frontend/src/app/frontcraft/pages/people/tree-grid-shared.scss
  56. 64
    0
      src/frontend/src/app/frontcraft/pages/raid/archive.component.ts
  57. 30
    0
      src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.html
  58. 44
    0
      src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.ts
  59. 119
    0
      src/frontend/src/app/frontcraft/pages/raid/raid.component.html
  60. 123
    0
      src/frontend/src/app/frontcraft/pages/raid/raid.component.ts
  61. 28
    0
      src/frontend/src/app/frontcraft/pages/raids/createraid.component.html
  62. 59
    0
      src/frontend/src/app/frontcraft/pages/raids/createraid.compontent.ts
  63. 37
    0
      src/frontend/src/app/frontcraft/pages/raids/raids.component.html
  64. 7
    0
      src/frontend/src/app/frontcraft/pages/raids/raids.component.scss
  65. 54
    0
      src/frontend/src/app/frontcraft/pages/raids/raids.component.ts
  66. 31
    0
      src/frontend/src/app/frontcraft/pages/rules/rules.component.html
  67. 31
    0
      src/frontend/src/app/frontcraft/pages/rules/rules.component.ts
  68. 26
    0
      src/frontend/src/app/frontcraft/pages/shop/itemselect.component.html
  69. 45
    0
      src/frontend/src/app/frontcraft/pages/shop/itemselector.component.ts
  70. 13
    0
      src/frontend/src/app/frontcraft/pages/shop/shop.component.html
  71. 135
    0
      src/frontend/src/app/frontcraft/pages/shop/shop.component.ts
  72. 16
    2
      src/frontend/src/app/frontcraft/pages/user/user.component.html
  73. 19
    20
      src/frontend/src/app/frontcraft/pages/user/user.component.ts
  74. 26
    15
      src/frontend/src/app/frontcraft/services/login-api.ts
  75. 1
    1
      src/frontend/tsconfig.json

+ 38
- 19
package-lock.json View File

714
       "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
714
       "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
715
     },
715
     },
716
     "aws4": {
716
     "aws4": {
717
-      "version": "1.8.0",
718
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
719
-      "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
717
+      "version": "1.9.1",
718
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
719
+      "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
720
     },
720
     },
721
     "balanced-match": {
721
     "balanced-match": {
722
       "version": "1.0.0",
722
       "version": "1.0.0",
1166
         "safe-buffer": "^5.0.1"
1166
         "safe-buffer": "^5.0.1"
1167
       }
1167
       }
1168
     },
1168
     },
1169
+    "circular-buffer": {
1170
+      "version": "1.0.2",
1171
+      "resolved": "https://registry.npmjs.org/circular-buffer/-/circular-buffer-1.0.2.tgz",
1172
+      "integrity": "sha1-+g4VLtYp92/iTd+J5y69AHhsWm4="
1173
+    },
1169
     "class-utils": {
1174
     "class-utils": {
1170
       "version": "0.3.6",
1175
       "version": "0.3.6",
1171
       "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
1176
       "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
1465
         "randomfill": "^1.0.3"
1470
         "randomfill": "^1.0.3"
1466
       }
1471
       }
1467
     },
1472
     },
1473
+    "crypto-js": {
1474
+      "version": "3.1.9-1",
1475
+      "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz",
1476
+      "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg="
1477
+    },
1468
     "cyclist": {
1478
     "cyclist": {
1469
       "version": "0.2.2",
1479
       "version": "0.2.2",
1470
       "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
1480
       "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
5031
       "dev": true
5041
       "dev": true
5032
     },
5042
     },
5033
     "npm-bundled": {
5043
     "npm-bundled": {
5034
-      "version": "1.0.6",
5035
-      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz",
5036
-      "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g=="
5044
+      "version": "1.1.1",
5045
+      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
5046
+      "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
5047
+      "requires": {
5048
+        "npm-normalize-package-bin": "^1.0.1"
5049
+      }
5050
+    },
5051
+    "npm-normalize-package-bin": {
5052
+      "version": "1.0.1",
5053
+      "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
5054
+      "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
5037
     },
5055
     },
5038
     "npm-packlist": {
5056
     "npm-packlist": {
5039
-      "version": "1.4.6",
5040
-      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.6.tgz",
5041
-      "integrity": "sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg==",
5057
+      "version": "1.4.8",
5058
+      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
5059
+      "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
5042
       "requires": {
5060
       "requires": {
5043
         "ignore-walk": "^3.0.1",
5061
         "ignore-walk": "^3.0.1",
5044
-        "npm-bundled": "^1.0.1"
5062
+        "npm-bundled": "^1.0.1",
5063
+        "npm-normalize-package-bin": "^1.0.1"
5045
       }
5064
       }
5046
     },
5065
     },
5047
     "npm-run-path": {
5066
     "npm-run-path": {
5752
       "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
5771
       "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
5753
     },
5772
     },
5754
     "psl": {
5773
     "psl": {
5755
-      "version": "1.4.0",
5756
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz",
5757
-      "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw=="
5774
+      "version": "1.7.0",
5775
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
5776
+      "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
5758
     },
5777
     },
5759
     "public-encrypt": {
5778
     "public-encrypt": {
5760
       "version": "4.0.3",
5779
       "version": "4.0.3",
6111
       }
6130
       }
6112
     },
6131
     },
6113
     "rpclibrary": {
6132
     "rpclibrary": {
6114
-      "version": "1.6.2",
6115
-      "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.6.2.tgz",
6116
-      "integrity": "sha512-lQTU4XkB9CSHz7YgtAcpVfyR5XrmTRX4P4eQjK6DDUjqKSFvJb5ChXjHnt1BcaNWbJ2VqmhCNVbcoyunJ2u7Rg==",
6133
+      "version": "1.7.0",
6134
+      "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.7.0.tgz",
6135
+      "integrity": "sha512-HIL5YzFF53fACOWvI403hJZnGYoXLJGrGc0n1HYZH6rnnfEaBcogemZrCbyoOvnG204LH882e8VdpB+WBTEY/g==",
6117
       "requires": {
6136
       "requires": {
6118
         "bsock": "^0.1.9",
6137
         "bsock": "^0.1.9",
6119
         "http": "0.0.0",
6138
         "http": "0.0.0",
6518
       "dev": true
6537
       "dev": true
6519
     },
6538
     },
6520
     "sqlite3": {
6539
     "sqlite3": {
6521
-      "version": "4.1.0",
6522
-      "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.1.0.tgz",
6523
-      "integrity": "sha512-RvqoKxq+8pDHsJo7aXxsFR18i+dU2Wp5o12qAJOV5LNcDt+fgJsc2QKKg3sIRfXrN9ZjzY1T7SNe/DFVqAXjaw==",
6540
+      "version": "4.1.1",
6541
+      "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.1.1.tgz",
6542
+      "integrity": "sha512-CvT5XY+MWnn0HkbwVKJAyWEMfzpAPwnTiB3TobA5Mri44SrTovmmh499NPQP+gatkeOipqPlBLel7rn4E/PCQg==",
6524
       "requires": {
6543
       "requires": {
6525
         "nan": "^2.12.1",
6544
         "nan": "^2.12.1",
6526
         "node-pre-gyp": "^0.11.0",
6545
         "node-pre-gyp": "^0.11.0",

+ 4
- 2
package.json View File

26
     "bsert": "0.0.10",
26
     "bsert": "0.0.10",
27
     "bsock": "^0.1.9",
27
     "bsock": "^0.1.9",
28
     "child-process-promise": "^2.2.1",
28
     "child-process-promise": "^2.2.1",
29
+    "circular-buffer": "^1.0.2",
30
+    "crypto-js": "^3.1.9-1",
29
     "debug": "^4.1.1",
31
     "debug": "^4.1.1",
30
     "express": "^4.16.4",
32
     "express": "^4.16.4",
31
     "frontblock": "^0.15.2",
33
     "frontblock": "^0.15.2",
41
     "path": "^0.12.7",
43
     "path": "^0.12.7",
42
     "reflect-metadata": "^0.1.13",
44
     "reflect-metadata": "^0.1.13",
43
     "rimraf": "^3.0.0",
45
     "rimraf": "^3.0.0",
44
-    "rpclibrary": "^1.6.2",
46
+    "rpclibrary": "^1.7.0",
45
     "simple-git": "^1.124.0",
47
     "simple-git": "^1.124.0",
46
     "spawn-sync": "^2.0.0",
48
     "spawn-sync": "^2.0.0",
47
-    "sqlite3": "^4.1.0",
49
+    "sqlite3": "^4.1.1",
48
     "trash": "^6.0.0",
50
     "trash": "^6.0.0",
49
     "tsyringe": "^4.0.1",
51
     "tsyringe": "^4.0.1",
50
     "upgiter": "^1.0.4",
52
     "upgiter": "^1.0.4",

+ 11
- 5
src/backend/Admin/Admin.ts View File

11
 import { ItemManager } from '../Components/Item/ItemManager';
11
 import { ItemManager } from '../Components/Item/ItemManager';
12
 import { RaidManager } from '../Components/Raid/RaidManager';
12
 import { RaidManager } from '../Components/Raid/RaidManager';
13
 import { CharacterManager } from '../Components/Character/CharacterManager';
13
 import { CharacterManager } from '../Components/Character/CharacterManager';
14
-import { LoginManager } from '../Components/Login/LoginManager';
14
+import { UserManager } from '../Components/User/UserManager';
15
 import { RootComponent } from '../Injector/ServiceDecorator';
15
 import { RootComponent } from '../Injector/ServiceDecorator';
16
 import { TableDefinitionExporter } from '../Types/Interfaces';
16
 import { TableDefinitionExporter } from '../Types/Interfaces';
17
 import { AdminConf, TableDefiniton } from '../Types/Types';
17
 import { AdminConf, TableDefiniton } from '../Types/Types';
19
 import { FrontworkComponent } from '../Types/FrontworkComponent';
19
 import { FrontworkComponent } from '../Types/FrontworkComponent';
20
 import { IAdmin } from './Interface';
20
 import { IAdmin } from './Interface';
21
 import { Injector } from '../Injector/Injector';
21
 import { Injector } from '../Injector/Injector';
22
+import { Shoutbox } from '../Components/Shoutbox/Shoutbox';
22
 
23
 
23
 
24
 
24
 const logger = getLogger("admin", 'debug') 
25
 const logger = getLogger("admin", 'debug') 
25
 
26
 
26
 @RootComponent({
27
 @RootComponent({
27
-    implements: IAdmin,
28
-    imports: [
28
+    injectable: IAdmin,
29
+    injects: [
29
         GuildManager,
30
         GuildManager,
30
         ItemManager,
31
         ItemManager,
31
         RaidManager,
32
         RaidManager,
32
         CharacterManager,
33
         CharacterManager,
33
-        LoginManager
34
+        UserManager,
35
+        Shoutbox
34
     ]
36
     ]
35
 })
37
 })
36
 export class FrontworkAdmin 
38
 export class FrontworkAdmin 
169
         }
171
         }
170
 
172
 
171
         this.knex = Knex(conf)
173
         this.knex = Knex(conf)
172
-    
174
+        if(conf.client === 'sqlite3'){
175
+            await this.knex.raw('PRAGMA foreign_keys = ON');
176
+        
177
+        }
178
+
173
         await Promise.all(
179
         await Promise.all(
174
             this.getTableDefinitions()
180
             this.getTableDefinitions()
175
             //make unique by name
181
             //make unique by name

+ 1
- 1
src/backend/Admin/Interface.ts View File

1
 import { RPCConfigLoader } from "../Components/RPCConfigLoader"
1
 import { RPCConfigLoader } from "../Components/RPCConfigLoader"
2
 import { AdminConf } from "../Types/Types"
2
 import { AdminConf } from "../Types/Types"
3
 import { RPCServer } from "rpclibrary"
3
 import { RPCServer } from "rpclibrary"
4
-import Knex = require("knex")
4
+import * as Knex from "knex"
5
 
5
 
6
 export class IAdmin{
6
 export class IAdmin{
7
     knex: Knex
7
     knex: Knex

+ 45
- 34
src/backend/Components/Character/CharacterManager.ts View File

1
 import { RPCInterface } from "rpclibrary";
1
 import { RPCInterface } from "rpclibrary";
2
-import { Inject, Module } from "../../Injector/ServiceDecorator";
3
-import { TableDefiniton, Character, Spec, User } from "../../Types/Types";
2
+import { Inject, Injectable } from "../../Injector/ServiceDecorator";
3
+import { TableDefiniton, Character, Spec, User, Class, _Rank, Rank } from "../../Types/Types";
4
 import { CharacterManagerIfc } from "./RPCInterface";
4
 import { CharacterManagerIfc } from "./RPCInterface";
5
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
5
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
6
 import { getSpecTableData, SpecT } from "../../Types/PlayerSpecs";
6
 import { getSpecTableData, SpecT } from "../../Types/PlayerSpecs";
7
 import { IAdmin } from "../../Admin/Interface";
7
 import { IAdmin } from "../../Admin/Interface";
8
-import { ILoginManager } from "../Login/Interface";
8
+import { IUserManager } from "../User/Interface";
9
 import { ICharacterManager } from "./Interface";
9
 import { ICharacterManager } from "./Interface";
10
-import { getLogger } from "log4js";
11
 
10
 
12
-@Module(ICharacterManager)
11
+@Injectable(ICharacterManager)
13
 export class CharacterManager
12
 export class CharacterManager
14
 implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterManager{
13
 implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterManager{
15
     name = "CharacterManager" as "CharacterManager";   
14
     name = "CharacterManager" as "CharacterManager";   
17
     @Inject(IAdmin)
16
     @Inject(IAdmin)
18
     private admin: IAdmin   
17
     private admin: IAdmin   
19
 
18
 
20
-    @Inject(ILoginManager)
21
-    private loginManager : ILoginManager
19
+    @Inject(IUserManager)
20
+    private loginManager : IUserManager
22
 
21
 
23
     exportRPCs = () => [
22
     exportRPCs = () => [
24
-        {
25
-            name: 'getSpecId' as 'getSpecId',
26
-            call: this.getSpecId
27
-        },{
28
-            name: 'getCharacterByName' as 'getCharacterByName',
29
-            call: this.getCharacterByName
30
-        },{
31
-            name: 'getCharacters' as 'getCharacters',
32
-            call: this.getCharacters
33
-        },{
34
-            name: 'getCharactersOfUser' as 'getCharactersOfUser',
35
-            call: this.getCharactersOfUser
36
-        }
23
+            this.getSpecId,
24
+            this.getCharacterByName,
25
+            this.getCharacters,
26
+            this.getCharactersOfUser,
27
+            this.createCharacter,
28
+            this.getUserOfCharacter,
29
+            this.getHeadCount
37
     ]
30
     ]
38
 
31
 
39
     exportRPCFeatures = () => [
32
     exportRPCFeatures = () => [
40
-        {
41
-            name: "createCharacter",
42
-            exportRPCs: () => [
43
-                {
44
-                    name: 'createCharacter',
45
-                    call: this.createCharacter
46
-                }
47
-            ]
48
-        }
49
     ]
33
     ]
50
     
34
     
51
     getTableDefinitions = (): TableDefiniton[] => [
35
     getTableDefinitions = (): TableDefiniton[] => [
58
                 table.foreign("specid").references("specs.id")
42
                 table.foreign("specid").references("specs.id")
59
                 table.integer("userid").notNullable()
43
                 table.integer("userid").notNullable()
60
                 table.foreign("userid").references("users.id")
44
                 table.foreign("userid").references("users.id")
45
+                table.string("race").notNullable()
46
+                table.boolean('alt').defaultTo(false)
61
             }
47
             }
62
         },{
48
         },{
63
             name: 'specs',
49
             name: 'specs',
87
             return char
73
             return char
88
         }catch(e){
74
         }catch(e){
89
             console.log(e);
75
             console.log(e);
90
-            
91
         }
76
         }
92
         throw new Error('Unable to create character')
77
         throw new Error('Unable to create character')
93
     }
78
     }
94
 
79
 
95
-    getCharacters = async() : Promise<Character[]> => {
96
-        return this.admin.knex.select('*').from('characters')
80
+    getCharacters = async() : Promise<(Character & Spec & User)[]> => {
81
+        return this.admin.knex
82
+        .select('*')
83
+        .from('characters as c')
84
+        .join('specs as sp', 'c.specid', 'sp.id')
85
+        .join('users as u', 'c.userid', 'u.id')
97
     }
86
     }
98
 
87
 
99
     getCharacterByName = async(charactername: string) : Promise<(Character & User & Spec) | void> => {
88
     getCharacterByName = async(charactername: string) : Promise<(Character & User & Spec) | void> => {
101
         return await this.admin.knex('characters as c')
90
         return await this.admin.knex('characters as c')
102
         .join('specs as s', 's.id', '=', 'c.specid')
91
         .join('specs as s', 's.id', '=', 'c.specid')
103
         .join('users as u', 'u.id', '=', 'c.userid')
92
         .join('users as u', 'u.id', '=', 'c.userid')
104
-        .select('charactername', 'class', 'specname', 'username', 'rank', 'locked', )
93
+        .select('c.id as id', 'charactername', 'race', 'specid', 'class', 'specname', 'username', 'rank')
105
         .where('charactername', '=', charactername)
94
         .where('charactername', '=', charactername)
106
         .first()
95
         .first()
107
     }
96
     }
111
         return await this.admin.knex('characters as c')
100
         return await this.admin.knex('characters as c')
112
         .join('users as u', 'u.id', '=', 'c.userid')
101
         .join('users as u', 'u.id', '=', 'c.userid')
113
         .join('specs as s', 's.id', '=', 'c.specid')
102
         .join('specs as s', 's.id', '=', 'c.specid')
114
-        .select('class', 'charactername', 'class', 'specname')
103
+        .select('class', 'charactername', 'class', 'race', 'specname', 'userid', 'c.id as id', 'specid')
115
         .where('u.username', '=', username)
104
         .where('u.username', '=', username)
116
     }
105
     }
117
 
106
 
107
+    getUserOfCharacter = async(character: Character) : Promise<User> => {
108
+        return await this.admin.knex('users').where({
109
+            id: character.userid
110
+        }).first()
111
+    }
112
+
118
     getSpecId = async <c extends keyof SpecT>(clazz: c, name: SpecT[c]) => await this.admin.knex
113
     getSpecId = async <c extends keyof SpecT>(clazz: c, name: SpecT[c]) => await this.admin.knex
119
     .from('specs')
114
     .from('specs')
120
     .select('id')
115
     .select('id')
125
     .first()
120
     .first()
126
     .then(spec => spec.id)
121
     .then(spec => spec.id)
127
 
122
 
123
+    getHeadCount = async(c:Class):Promise<number> => {
124
+        const cnt = await this.admin
125
+        .knex('characters as c')
126
+        .join('users as u', 'c.userid', '=', 'u.id')
127
+        .join('specs as s', 'c.specid', '=', 's.id')
128
+        .where({
129
+            class: c
130
+        }).andWhere(function(){
131
+            this.whereNotIn('u.rank', <Rank[]>[
132
+                'Guest'
133
+            ])
134
+            this.whereNot('c.alt', '1')
135
+        })
136
+        .count('*')
137
+        return cnt[0]['count(*)'] as number
138
+    }
128
 }
139
 }

+ 4
- 2
src/backend/Components/Character/Interface.ts View File

1
-import { Character, Spec, User } from "../../Types/Types"
1
+import { Character, Spec, User, Class } from "../../Types/Types"
2
 import { SpecT } from "../../Types/PlayerSpecs"
2
 import { SpecT } from "../../Types/PlayerSpecs"
3
 
3
 
4
 
4
 
5
 export class ICharacterManager{
5
 export class ICharacterManager{
6
     createCharacter: (usertoken: string, char : Character) => Promise<Character>
6
     createCharacter: (usertoken: string, char : Character) => Promise<Character>
7
     getSpecId: <c extends keyof SpecT>(clazz: c, name: SpecT[c]) => Promise<number>
7
     getSpecId: <c extends keyof SpecT>(clazz: c, name: SpecT[c]) => Promise<number>
8
-    getCharacters: () => Promise<Character[]>
8
+    getCharacters: () => Promise<(Character & Spec & User)[]>
9
     getCharacterByName: (charactername: string) => Promise<(Character & User & Spec) | void>
9
     getCharacterByName: (charactername: string) => Promise<(Character & User & Spec) | void>
10
     getCharactersOfUser: (username: string) => Promise<(Character & Spec)[]>
10
     getCharactersOfUser: (username: string) => Promise<(Character & Spec)[]>
11
+    getUserOfCharacter: (character:Character) => Promise<User>
12
+    getHeadCount: (clazz: Class) => Promise<number>
11
 }
13
 }

+ 4
- 3
src/backend/Components/Character/RPCInterface.ts View File

7
         getCharacters : ICharacterManager['getCharacters']
7
         getCharacters : ICharacterManager['getCharacters']
8
         getCharacterByName : ICharacterManager['getCharacterByName']
8
         getCharacterByName : ICharacterManager['getCharacterByName']
9
         getCharactersOfUser: ICharacterManager['getCharactersOfUser']
9
         getCharactersOfUser: ICharacterManager['getCharactersOfUser']
10
+        createCharacter: ICharacterManager['createCharacter']
11
+        getUserOfCharacter: ICharacterManager['getUserOfCharacter']
12
+        getHeadCount: ICharacterManager['getHeadCount']
10
     }
13
     }
11
 }
14
 }
12
 
15
 
13
 export type CharacterManagerFeatureIfc = {
16
 export type CharacterManagerFeatureIfc = {
14
-    createCharacter: {
15
-        createCharacter: ICharacterManager['createCharacter']
16
-    }
17
+    
17
 }
18
 }

+ 0
- 34
src/backend/Components/Debugger/Debugger.ts View File

1
-import { RPCExporter } from "rpclibrary"
2
-import { FrontworkAdmin } from "../../Admin/Admin"
3
-import { TableDefiniton } from "../../Types/Types"
4
-import { PrivilegedRPCExporter } from "../../Types/PrivilegedRPCExporter"
5
-import { FrontworkComponent } from "../../Types/FrontworkComponent"
6
-
7
-export class Debugger 
8
-implements FrontworkComponent<any,any>{
9
-    public admin: FrontworkAdmin
10
-    name = "Debugger" 
11
-    
12
-
13
-    constructor(private exporters: PrivilegedRPCExporter[]){
14
-    }
15
-
16
-    exportRPCs(){
17
-        
18
-        return [{
19
-            name: 'getTable',
20
-            call: async(table:string) => this.admin.knex.select('*').from(table)
21
-        }, 
22
-        ...this.exporters.flatMap(e => e.exportRPCs()),
23
-        ...this.exporters.flatMap(e => e.exportRPCFeatures().flatMap(e => e.exportRPCs()))]
24
-    }
25
-
26
-    exportRPCFeatures(): RPCExporter<any, any, {}>[] {
27
-        return []
28
-    }
29
-
30
-    getTableDefinitions(): TableDefiniton[] {
31
-        return []
32
-    }
33
-
34
-}

+ 12
- 20
src/backend/Components/Guild/GuildManager.ts View File

1
 import { ConfigLoader } from "loadson";
1
 import { ConfigLoader } from "loadson";
2
-import { Inject, Module } from "../../Injector/ServiceDecorator";
2
+import { Inject, Injectable } from "../../Injector/ServiceDecorator";
3
 import { GuildManagerFeatureIfc, GuildManagerIfc } from "./RPCInterface";
3
 import { GuildManagerFeatureIfc, GuildManagerIfc } from "./RPCInterface";
4
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
4
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
5
 import { _Rank, Rank } from "../../Types/Types";
5
 import { _Rank, Rank } from "../../Types/Types";
10
     name: string
10
     name: string
11
     realm: string
11
     realm: string
12
     description: string
12
     description: string
13
-}
13
+};
14
 
14
 
15
-@Module(IGuildManager)
15
+@Injectable(IGuildManager)
16
 export class GuildManager
16
 export class GuildManager
17
 implements FrontworkComponent<GuildManagerIfc, GuildManagerFeatureIfc>, IGuildManager{
17
 implements FrontworkComponent<GuildManagerIfc, GuildManagerFeatureIfc>, IGuildManager{
18
 
18
 
32
         }
32
         }
33
     }, "./config")
33
     }, "./config")
34
 
34
 
35
-    exportRPCs = () => [{
36
-        name: 'getHeadCount' as 'getHeadCount',
37
-        call: this.getHeadCount
38
-    },{
39
-        name: 'getGuildInfo' as 'getGuildInfo',
40
-        call: this.getGuildInfo
41
-    }]
35
+    exportRPCs = () => [
36
+        this.getHeadCount,
37
+        this.getGuildInfo
38
+    ]
42
 
39
 
43
     exportRPCFeatures = () => [{
40
     exportRPCFeatures = () => [{
44
         name: 'manageGuild' as 'manageGuild',
41
         name: 'manageGuild' as 'manageGuild',
45
-        exportRPCs: () => [{
46
-            name: 'setName' as 'setName',
47
-            call: this.setName
48
-        },{
49
-            name: 'setRealm' as 'setRealm',
50
-            call: this.setRealm
51
-        }, {
52
-            name: 'setDescription' as 'setDescription',
53
-            call: this.setDescription
54
-        }]
42
+        exportRPCs: () => [
43
+            this.setName,
44
+            this.setRealm,
45
+            this.setDescription
46
+        ]
55
     }]
47
     }]
56
     
48
     
57
     getTableDefinitions = () => []
49
     getTableDefinitions = () => []

+ 10
- 2
src/backend/Components/Item/Interface.ts View File

1
-import { Item } from "../../Types/Types"
1
+import { Item, Character, SRToken, SRPriority, Spec } from "../../Types/Types"
2
 
2
 
3
 export class IItemManager{
3
 export class IItemManager{
4
     getItems: () => Promise<Item[]>
4
     getItems: () => Promise<Item[]>
5
-    getItem: (name:string) => Promise<Item>
5
+    fetchItem: (name:string) => Promise<Item>
6
+    buyToken: (usertoken: string, charactername:string, itemname:string) => Promise<(SRToken & Character & Item) | void>
7
+    setPriority: (itemname:string, priority: any) => Promise<void>
8
+    calculatePriorities: (itemname: string, character:Character) => Promise<number>
9
+    deletePriority: (priority:SRPriority) => Promise<void>
10
+    getTokens: (character:Character) => Promise<SRToken[]>
11
+    getToken: (character:Character, item:Item) => Promise<(SRToken & Character & Item) | void>
12
+    getAllPriorities: () => Promise<(SRPriority & Spec & Item)[]>
13
+    wipeCurrencyAndItems: () => Promise<void>
6
 }
14
 }

+ 183
- 22
src/backend/Components/Item/ItemManager.ts View File

1
 import { T1 } from "../../Types/Items";
1
 import { T1 } from "../../Types/Items";
2
-import { RPC } from "rpclibrary";
3
-import { Inject, Module } from "../../Injector/ServiceDecorator";
2
+import { Inject, Injectable } from "../../Injector/ServiceDecorator";
4
 import { ItemManagerFeatureIfc, ItemManagerIfc } from "./RPCInterface";
3
 import { ItemManagerFeatureIfc, ItemManagerIfc } from "./RPCInterface";
5
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
4
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
6
 import { TableDefinitionExporter } from "../../Types/Interfaces";
5
 import { TableDefinitionExporter } from "../../Types/Interfaces";
7
-import { TableDefiniton, Item } from "../../Types/Types";
6
+import { TableDefiniton, Item, User, Character, SRToken, SRPriority, Spec } from "../../Types/Types";
8
 import { IAdmin } from "../../Admin/Interface";
7
 import { IAdmin } from "../../Admin/Interface";
9
 import { IItemManager } from "./Interface";
8
 import { IItemManager } from "./Interface";
10
 import { getLogger } from "log4js";
9
 import { getLogger } from "log4js";
10
+import { IUserManager } from "../User/Interface";
11
+import { ICharacterManager } from "../Character/Interface";
12
+import { join } from "path";
11
 
13
 
12
 const fetch = require('node-fetch')
14
 const fetch = require('node-fetch')
13
 const xml2js = require('xml2js');
15
 const xml2js = require('xml2js');
14
 const parser = new xml2js.Parser(/* options */);
16
 const parser = new xml2js.Parser(/* options */);
15
 
17
 
16
-@Module(IItemManager)
18
+@Injectable(IItemManager)
17
 export class ItemManager
19
 export class ItemManager
18
 implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefinitionExporter, IItemManager{
20
 implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefinitionExporter, IItemManager{
19
     name = "ItemManager" as "ItemManager";    
21
     name = "ItemManager" as "ItemManager";    
21
     @Inject(IAdmin)
23
     @Inject(IAdmin)
22
     private admin: IAdmin
24
     private admin: IAdmin
23
 
25
 
24
-    exportRPCs(): RPC<any, any>[]{
25
-        return [{
26
-            name: 'getItems',
27
-            call: this.getItems
28
-        },{
29
-            name: 'getItem',
30
-            call: this.getItem
31
-        }]
32
-    }
26
+    @Inject(IUserManager)
27
+    private userManager: IUserManager
28
+
29
+    @Inject(ICharacterManager)
30
+    private character: ICharacterManager
31
+
32
+    exportRPCs = () => [
33
+        this.getItems,
34
+        this.getItem,
35
+        this.buyToken,
36
+        this.calculatePriorities,
37
+        this.getToken,
38
+        this.getTokens,
39
+        this.getAllPriorities
40
+    ]
33
 
41
 
34
-    exportRPCFeatures() {
35
-        return []
42
+    exportRPCFeatures = () => [{
43
+        name: 'managePriorities' as 'managePriorities',
44
+        exportRPCs: () => [
45
+            this.setPriority,
46
+            this.deletePriority
47
+        ],
48
+    },{
49
+        name: 'reset' as 'reset',
50
+        exportRPCs: () => [
51
+            this.wipeCurrencyAndItems
52
+        ]
53
+    }]
54
+
55
+    wipeCurrencyAndItems = async () => {
56
+        await Promise.all([
57
+            this.userManager.wipeCurrency(),
58
+            this.admin.knex('tokens').where(true).del()
59
+        ])
36
     }
60
     }
37
 
61
 
38
     getItems = async () :Promise<Item[]> => await this.admin.knex.select('*').from('items')
62
     getItems = async () :Promise<Item[]> => await this.admin.knex.select('*').from('items')
39
 
63
 
40
-    getItem  = async (name:string):Promise<Item> => {
64
+    getItem = async(name:string):Promise<Item> => {
65
+        return await this.admin.knex('items').select('*').where('itemname','=',name).first()
66
+    }
67
+
68
+    fetchItem  = async (name:string):Promise<Item> => {
41
         const res = await fetch('https://classic.wowhead.com/item='+name+'&xml'); 
69
         const res = await fetch('https://classic.wowhead.com/item='+name+'&xml'); 
42
         const txt = await res.text(); 
70
         const txt = await res.text(); 
43
         const r = await parser.parseStringPromise(txt); 
71
         const r = await parser.parseStringPromise(txt); 
60
             {
88
             {
61
                 name: 'tokens',
89
                 name: 'tokens',
62
                 tableBuilder: (table) => {
90
                 tableBuilder: (table) => {
63
-                    table.integer("characterid").primary()
91
+                    table.primary(['characterid', 'itemid'])
92
+                    table.integer("characterid")
64
                     table.foreign("characterid").references("id").inTable('characters')
93
                     table.foreign("characterid").references("id").inTable('characters')
65
-                    table.integer("itemid").primary()
94
+                    table.integer("itemid")
66
                     table.foreign("itemid").references("id").inTable('items')
95
                     table.foreign("itemid").references("id").inTable('items')
67
-                    table.integer("level")
96
+                    table.integer("level").defaultTo(1)
97
+                }
98
+            },{
99
+                name: 'priorities',
100
+                tableBuilder: (table) => {
101
+                    table.increments('id').primary()
102
+                    table.integer('itemid')
103
+                    table.foreign('itemid').references('id').inTable('items')
104
+                    table.string('race').nullable()
105
+                    table.integer('specid').nullable()
106
+                    table.foreign('specid').references('id').inTable('specs')
107
+                    table.integer('modifier')
108
+                    table.string('description').nullable()
68
                 }
109
                 }
69
             },{
110
             },{
70
                 name: 'items',
111
                 name: 'items',
79
             }]
120
             }]
80
     }
121
     }
81
 
122
 
123
+    buyToken = async (usertoken: string, charactername:string, itemname:string): Promise<(SRToken & Character & Item) | void> => {
124
+        const record = this.userManager.getUserRecordByToken(usertoken)
125
+        const character = await this.character.getCharacterByName(charactername)
126
+
127
+        if(!record || !character || record.user.username !== character.username) return
128
+      
129
+        const item = await this.getItem(itemname)
130
+        if(!item) return
131
+
132
+        const currency = await this.userManager.getCurrency(record.user)
133
+        if(currency < 1) return
134
+
135
+        const existingToken = await this.getToken(character, item)
136
+
137
+        await this.userManager.decrementCurrency(record.user, 1)
138
+
139
+        if(!existingToken){
140
+            const modifier = await this.calculatePriorities(itemname, character)
141
+            await this.admin.knex('tokens').insert({
142
+                characterid: character.id,
143
+                itemid: item.id,
144
+                level: 1+modifier
145
+            })
146
+        }else{
147
+            await this.admin.knex('tokens').where({
148
+                characterid: character.id,
149
+                itemid: item.id
150
+            }).increment('level')
151
+        }
152
+
153
+        return await this.getToken(character, item)
154
+    }
155
+
156
+    getAllPriorities = async() :Promise<(SRPriority & Spec & Item)[]> => this.admin
157
+    .knex('priorities as p')
158
+    .join('items as i', 'p.itemid', '=', 'i.id')
159
+    .leftJoin('specs as s', 'p.specid', '=', 's.id')
160
+    .select('*') 
161
+
162
+    deletePriority = async(priority:SRPriority) : Promise<void> => {
163
+        await this.admin.knex('priorities')
164
+        .where(priority)
165
+        .del()
166
+    }
167
+
168
+    setPriority = async(itemname:string, priority: SRPriority) : Promise<void> => {
169
+        const item = await this.getItem(itemname)
170
+      
171
+        await this.admin
172
+        .knex('priorities')
173
+        .insert(<SRPriority>{
174
+            itemid: item.id,
175
+            ...priority
176
+        })
177
+    }
178
+
179
+    getPriorities = async(itemname:string) : Promise<SRPriority[]> => {
180
+        const item = await this.getItem(itemname)
181
+
182
+        return await this.admin.knex('priorities')
183
+        .where('itemid', '=', item.id)
184
+        .select('*')
185
+    }
186
+
187
+    calculatePriorities = async (itemname: string, character:Character):Promise<number>=> {
188
+        const rules : SRPriority[] = await this.admin.knex('priorities as p')
189
+        .select('*')
190
+        .join('items as i', 'i.id', '=', 'p.itemid')
191
+        .where('itemname', '=', itemname)
192
+
193
+        return rules.map(rule => {
194
+            if(rule.specid && rule.race){
195
+                if(rule.specid === character.specid && rule.race === character.race) 
196
+                    return rule.modifier
197
+                else
198
+                    return 0
199
+            }
200
+
201
+            if(rule.specid){
202
+                if(rule.specid === character.specid)
203
+                    return rule.modifier
204
+                else
205
+                    return 0
206
+            }
207
+
208
+            if(rule.race){
209
+                if(rule.race === character.race) 
210
+                    return rule.modifier
211
+                else
212
+                    return 0
213
+            }
214
+            return 0
215
+        }).reduce((prev, curr) => prev+curr, 0)
216
+    }
217
+
218
+    getToken = async (character:Character, item:Item): Promise<(SRToken & Character & Item) | void>=> {
219
+        return await this.admin
220
+        .knex('tokens as t')
221
+        .select('*')
222
+        .join('characters as c', 'c.id', '=', 't.characterid')
223
+        .join('items as i', 'i.id', '=', 't.itemid')
224
+        .where({
225
+            characterid: character.id,
226
+            itemid: item.id
227
+        })
228
+        .first()
229
+    }
230
+
231
+    getTokens = async (character:Character) : Promise<(SRToken & Character & Item)[]> => {
232
+        return await this.admin
233
+        .knex('tokens as t')
234
+        .select('*')
235
+        .select('*')
236
+        .join('characters as c', 'c.id', '=', 't.characterid')
237
+        .join('items as i', 'i.id', '=', 't.itemid')
238
+        .where({
239
+            characterid: character.id,
240
+        })
241
+    }
242
+
82
     countItems = async() :Promise<number> => {
243
     countItems = async() :Promise<number> => {
83
         const count = await this.admin.knex('items').count('*');
244
         const count = await this.admin.knex('items').count('*');
84
         return <number>count[0]['count(*)']
245
         return <number>count[0]['count(*)']
86
 
247
 
87
     private initialized = false
248
     private initialized = false
88
     initialize = async () => {
249
     initialize = async () => {
89
-        if(!this.initialized)
90
-            this.initialized = true        
250
+        if(this.initialized) return
251
+        this.initialized = true        
91
         
252
         
92
         const allItems = [...T1]
253
         const allItems = [...T1]
93
         getLogger('ItemManager').debug('Checking items')
254
         getLogger('ItemManager').debug('Checking items')
94
 
255
 
95
         const countCache = await this.countItems()
256
         const countCache = await this.countItems()
96
         if(countCache != allItems.length){
257
         if(countCache != allItems.length){
97
-            const items:Item[] = await Promise.all(allItems.map((i) => this.getItem(i)))
258
+            const items:Item[] = await Promise.all(allItems.map((i) => this.fetchItem(i)))
98
             try{
259
             try{
99
                 await this.admin
260
                 await this.admin
100
                 .knex('items')
261
                 .knex('items')

+ 13
- 1
src/backend/Components/Item/RPCInterface.ts View File

2
 
2
 
3
 export type ItemManagerIfc = {
3
 export type ItemManagerIfc = {
4
     ItemManager: {
4
     ItemManager: {
5
-        getItem: IItemManager['getItem']
5
+        getItem: IItemManager['fetchItem']
6
         getItems: IItemManager['getItems']
6
         getItems: IItemManager['getItems']
7
+        buyToken: IItemManager['buyToken']
8
+        calculatePriorities: IItemManager['calculatePriorities']
9
+        getToken: IItemManager['getToken']
10
+        getTokens: IItemManager['getTokens']
11
+        getAllPriorities: IItemManager['getAllPriorities']
7
     }
12
     }
8
 }
13
 }
9
 
14
 
10
 export type ItemManagerFeatureIfc = {
15
 export type ItemManagerFeatureIfc = {
16
+    reset: {
17
+        wipeCurrencyAndItems: IItemManager['wipeCurrencyAndItems']
18
+    }
19
+    managePriorities: {
20
+        setPriority:IItemManager['setPriority']
21
+        deletePriorits: IItemManager['deletePriority']
22
+    }
11
 }
23
 }

+ 0
- 18
src/backend/Components/Login/RPCInterface.ts View File

1
-import { ILoginManager } from "./Interface"
2
-
3
-export type LoginManagerIfc = {
4
-    Authenticator: {
5
-        checkToken: ILoginManager['checkToken']
6
-        login: ILoginManager['login']
7
-        logout: ILoginManager['logout']
8
-        createUser: ILoginManager['createUser']
9
-        getAuth: ILoginManager['getAuth']
10
-    }
11
-}
12
-
13
-export type LoginManagerFeatureIfc = {
14
-    modifyPermissions: {
15
-        setPermission: ILoginManager['setPermission']
16
-        getPermissions: ILoginManager['getPermissions']
17
-    }
18
-}

+ 8
- 21
src/backend/Components/RPCConfigLoader.ts View File

18
 
18
 
19
     name = "Config" as "Config"
19
     name = "Config" as "Config"
20
 
20
 
21
-    exportRPCs() {
22
-        return [{
23
-            name: "getConfig" as "getConfig",
24
-            call: this.getConfig
25
-        },{
26
-            name: "resetConfig" as "resetConfig",
27
-            call: this.resetConfig
28
-        },{
29
-            name: "setConfig" as "setConfig",
30
-            call: this.setConfig
31
-        },{
32
-            name: "setConfigKey" as "setConfigKey",
33
-            call: this.setConfigKey
34
-        },{
35
-            name: "deleteConfigKey" as "deleteConfigKey",
36
-            call: this.deleteConfigKey
37
-        },{
38
-            name: "getConfigKey" as "getConfigKey",
39
-            call: this.getConfigKey
40
-        }]
41
-    }
21
+    exportRPCs = () => [
22
+        this.getConfig,
23
+        this.resetConfig,
24
+        this.setConfig,
25
+        this.setConfigKey,
26
+        this.deleteConfigKey,
27
+        this.getConfigKey
28
+    ]
42
 }
29
 }

+ 9
- 2
src/backend/Components/Raid/Interface.ts View File

1
-import { Raid, Signup, Character } from "../../Types/Types"
1
+import { Raid, Signup, Character, RaidData } from "../../Types/Types"
2
 
2
 
3
 export class IRaidManager{
3
 export class IRaidManager{
4
     getRaids: () => Promise<Raid[]>
4
     getRaids: () => Promise<Raid[]>
6
     addSignup: (signup: Signup) => Promise<any>
6
     addSignup: (signup: Signup) => Promise<any>
7
     removeSignup: (signup: Signup) => Promise<any>    
7
     removeSignup: (signup: Signup) => Promise<any>    
8
     getSignups: (raid:Raid) => Promise<Signup[]>
8
     getSignups: (raid:Raid) => Promise<Signup[]>
9
-    sign: (userToken: string, character:Character, raid:Raid, attending:boolean) => Promise<any>
9
+    sign: (userToken: string, character:Character, raid:Raid, late:boolean) => Promise<any>
10
+    unsign: (userToken: string, character:Character, raid:Raid,) => Promise<any>
11
+    archiveRaid: (raid:Raid) => Promise<RaidData>
12
+    getRaidData: (raid:Raid) => Promise<RaidData>
13
+    setBenched: (signup: Signup) => Promise<void>
14
+    getPastRaids: (limit: number) => Promise<RaidData[]>
15
+    getArchiveRaid: (id:number) => Promise<RaidData>
16
+    startRaid: (raid:Raid) => Promise<RaidData>
10
 }
17
 }

+ 7
- 0
src/backend/Components/Raid/RPCInterface.ts View File

3
 export type RaidManagerIfc = {
3
 export type RaidManagerIfc = {
4
     RaidManager:{
4
     RaidManager:{
5
         getRaids: IRaidManager['getRaids']
5
         getRaids: IRaidManager['getRaids']
6
+        getRaidData: IRaidManager['getRaidData']
7
+        getPastRaids: IRaidManager['getPastRaids']
8
+        getArchiveRaid: IRaidManager['getArchiveRaid']
6
     }
9
     }
7
 }
10
 }
8
 
11
 
11
         createRaid: IRaidManager['createRaid']
14
         createRaid: IRaidManager['createRaid']
12
         addSignup: IRaidManager['addSignup']
15
         addSignup: IRaidManager['addSignup']
13
         removeSignup: IRaidManager['removeSignup'] 
16
         removeSignup: IRaidManager['removeSignup'] 
17
+        archiveRaid: IRaidManager['archiveRaid']
18
+        setBenched: IRaidManager['setBenched']
19
+        startRaid: IRaidManager['startRaid']
14
     }
20
     }
15
     signup: {
21
     signup: {
16
         getSignups: IRaidManager['getSignups']
22
         getSignups: IRaidManager['getSignups']
17
         sign: IRaidManager['sign']
23
         sign: IRaidManager['sign']
24
+        unsign: IRaidManager['unsign']
18
     }
25
     }
19
 }
26
 }

+ 237
- 39
src/backend/Components/Raid/RaidManager.ts View File

1
-import { Inject, Module } from "../../Injector/ServiceDecorator";
1
+import { Inject, Injectable } from "../../Injector/ServiceDecorator";
2
 import { RaidManagerIfc, RaidManagerFeatureIfc } from "./RPCInterface";
2
 import { RaidManagerIfc, RaidManagerFeatureIfc } from "./RPCInterface";
3
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
3
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
4
-import { TableDefiniton, Signup, Raid, User, Character } from "../../Types/Types";
4
+import { TableDefiniton, Signup, Raid, Character, RaidData, Spec, SRToken, Item } from "../../Types/Types";
5
 import { IAdmin } from "../../Admin/Interface";
5
 import { IAdmin } from "../../Admin/Interface";
6
 import { IRaidManager } from "./Interface";
6
 import { IRaidManager } from "./Interface";
7
-import { ILoginManager } from "../Login/Interface";
7
+import { IUserManager } from "../User/Interface";
8
+import { ICharacterManager } from "../Character/Interface";
8
 
9
 
9
-@Module(IRaidManager)
10
+@Injectable(IRaidManager)
10
 export class RaidManager
11
 export class RaidManager
11
 implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManager{
12
 implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManager{
12
     name = "RaidManager" as "RaidManager";    
13
     name = "RaidManager" as "RaidManager";    
14
     @Inject(IAdmin)
15
     @Inject(IAdmin)
15
     private admin: IAdmin
16
     private admin: IAdmin
16
 
17
 
17
-    @Inject(ILoginManager)
18
-    private login: ILoginManager
18
+    @Inject(IUserManager)
19
+    private userManager: IUserManager
19
 
20
 
20
-    exportRPCs = () => [{
21
-        name: 'getRaids' as 'getRaids',
22
-        call: this.getRaids
23
-    },]
21
+    @Inject(ICharacterManager)
22
+    private characterManager: ICharacterManager
23
+
24
+    exportRPCs = () => [
25
+        this.getRaids,
26
+        this.getRaidData,
27
+        this.getPastRaids,
28
+        this.getArchiveRaid
29
+    ]
24
 
30
 
25
     exportRPCFeatures() {
31
     exportRPCFeatures() {
26
         return [{
32
         return [{
27
             name: 'manageRaid' as 'manageRaid',
33
             name: 'manageRaid' as 'manageRaid',
28
-            exportRPCs: () => [{
29
-                name: 'createRaid' as 'createRaid',
30
-                call: this.createRaid
31
-            },{
32
-                name: 'addSignup' as 'addSignup',
33
-                call: this.addSignup
34
-            },{
35
-                name: 'removeSignup' as 'removeSignup',
36
-                call: this.removeSignup
37
-            }]
34
+            exportRPCs: () => [
35
+                this.createRaid,
36
+                this.addSignup,
37
+                this.removeSignup,
38
+                this.archiveRaid,
39
+                this.setBenched,
40
+                this.startRaid
41
+            ]
38
         },{
42
         },{
39
             name: 'signup' as 'signup',
43
             name: 'signup' as 'signup',
40
-            exportRPCs: () => [{
41
-                name: 'getSignups' as 'getSignups',
42
-                call: this.getSignups
43
-            },{
44
-                name: 'sign' as 'sign',
45
-                call: this.sign
46
-            }]
47
-        },]
44
+            exportRPCs: () => [
45
+                this.getSignups,
46
+                this.sign,
47
+                this.unsign
48
+            ]
49
+        }]
48
     }
50
     }
49
     
51
     
50
     getTableDefinitions(): TableDefiniton[] {
52
     getTableDefinitions(): TableDefiniton[] {
56
                     table.dateTime('start').notNullable()
58
                     table.dateTime('start').notNullable()
57
                     table.string('description').notNullable()
59
                     table.string('description').notNullable()
58
                     table.string('title').notNullable()
60
                     table.string('title').notNullable()
59
-                    table.integer('minrank').notNullable()
61
+                    table.integer('size').defaultTo(40)
62
+                }
63
+            },{
64
+                name: 'archive',
65
+                tableBuilder: (table) => {
66
+                    table.integer('id').primary()
67
+                    table.json('raiddata').notNullable()
60
                 }
68
                 }
61
             },{
69
             },{
62
                 name: 'signups',
70
                 name: 'signups',
63
                 tableBuilder: (table) => {
71
                 tableBuilder: (table) => {
64
                     table.primary(['raidid', 'characterid'])
72
                     table.primary(['raidid', 'characterid'])
65
                     table.integer('raidid')
73
                     table.integer('raidid')
66
-                    table.foreign('raidid').references('id').inTable('raids')
74
+                    table.foreign('raidid').references('id').inTable('raids').onDelete('CASCADE')
67
                     table.integer('characterid')
75
                     table.integer('characterid')
68
-                    table.foreign('characterid').references('id').inTable('characters')
76
+                    table.foreign('characterid').references('id').inTable('characters').onDelete('CASCADE')
77
+                    table.boolean('benched').defaultTo('false')
78
+                    table.boolean('late')
69
                 }
79
                 }
70
             }            
80
             }            
71
         ]
81
         ]
87
     })
97
     })
88
     .delete()
98
     .delete()
89
 
99
 
90
-    getRaids = async () : Promise<Raid[]> => await this.admin.knex
91
-    .select('*')
92
-    .from('raids')
100
+    getRaids = async () : Promise<Raid[]> => {
101
+
102
+        const subQuery = this.admin
103
+        .knex('signups')
104
+        .count('*')
105
+        .where({
106
+            raidid: this.admin.knex.ref('raids.id'),
107
+            benched: false,
108
+            late: false
109
+        })
110
+        .as('signupcount')
111
+        
112
+        return await this.admin.knex('raids')
113
+        .select('*', subQuery)
114
+        .orderBy('start', 'asc')
115
+    }
116
+
117
+    startRaid = async (raid:Raid) : Promise<RaidData> => {
118
+        const archived = await this.archiveRaid(raid)
119
+
120
+        delete archived.participants.late
121
+
122
+        const giveCurrency = async (b: Character) => {
123
+            const usr = await this.characterManager.getUserOfCharacter(b)
124
+            await this.userManager.incrementCurrency(usr, 1)
125
+        }
126
+
127
+        await Promise.all([
128
+            ...archived.participants.bench.map(giveCurrency),
129
+            ...Object.values(archived.participants).map((group: any) => group.map(giveCurrency))
130
+        ])
131
+
132
+        return archived
133
+    }
134
+
135
+    archiveRaid = async (raid:Raid) : Promise<RaidData> => {
136
+        const raidData = await this.getRaidData(raid)
137
+        await this.admin.knex('archive')
138
+        .insert({
139
+            id:raidData.id,
140
+            raiddata: JSON.stringify(raidData)
141
+        })
142
+        
143
+
144
+        await this.admin.knex('raids')
145
+        .where('id', '=', raid.id)
146
+        .del()
147
+    
148
+        const row = await this.admin.knex('archive')
149
+        .select('*')
150
+        .where({
151
+            id:raidData.id,
152
+        })
153
+        .first()
154
+
155
+        return JSON.parse(row.raiddata)
156
+    }
157
+
158
+    getArchiveRaid = async(id:number) : Promise<RaidData> => {
159
+        const data = await this.admin.knex('archive').select('raiddata').where({
160
+            id: id
161
+        }).first()
162
+        
163
+        return JSON.parse(data.raiddata)
164
+    }
165
+
166
+    getPastRaids = async(limit: number) : Promise<RaidData[]> => {
167
+        const raids = await this.admin.knex('archive')
168
+        .select('*')
169
+        .orderBy('id', 'desc')
170
+        .limit(limit)
171
+
172
+        return raids.map(raid => JSON.parse(raid.raiddata))
173
+    }
174
+
175
+    getRaidData = async (raid:Raid) : Promise<RaidData> => {
176
+        const ret = {
177
+            participants:{
178
+                Druid: <(Character&Spec)[]>[],
179
+                Hunter: <(Character&Spec)[]>[],
180
+                Mage: <(Character&Spec)[]>[],
181
+                Paladin: <(Character&Spec)[]>[],
182
+                Priest: <(Character&Spec)[]>[],
183
+                Rogue: <(Character&Spec)[]>[],
184
+                Shaman: <(Character&Spec)[]>[],
185
+                Warlock: <(Character&Spec)[]>[],
186
+                Warrior: <(Character&Spec)[]>[],
187
+                late: <(Character&Spec)[]>[],
188
+                bench: <(Character&Spec)[]>[],
189
+            },
190
+            tokens:{}
191
+        }
192
+
193
+        const subQuery = this.admin
194
+        .knex('signups')
195
+        .count('*')
196
+        .where({
197
+            raidid: this.admin.knex.ref('raids.id'),
198
+            benched: false,
199
+            late: false
200
+        })
201
+        .as('signupcount')
202
+
203
+        const raidInDb: Raid = await this.admin.knex('raids')
204
+        .select('*', subQuery)
205
+        .where('id','=',raid.id)
206
+        .first()
207
+
208
+        const characterData: (Character & Spec & Signup)[] = await this.admin
209
+        .knex('signups as s')
210
+        .select('characterid as id', 'charactername', 'class', 'specname', 'race', 'userid', 'benched', 'late', 'raidid', 'characterid')
211
+        .join('raids as r', 's.raidid','=','r.id')
212
+        .join('characters as c', 's.characterid','=','c.id')
213
+        .join('users as u', 'c.userid','=','u.id')
214
+        .join('specs as sp', 'specid','=','sp.id')
215
+        .where('r.id','=',raid.id)
216
+        
217
+        characterData.forEach(data => {
218
+            if(data.benched){
219
+                ret.participants.bench.push(data)
220
+                return
221
+            }
222
+            if(data.late){
223
+                ret.participants.late.push(data)
224
+                return
225
+            }
226
+            ret.participants[data.class].push(data)
227
+        })
228
+
229
+        const tokenData: (Character & SRToken & Item)[] = await this.admin
230
+        .knex('signups as s')
231
+        .select('*')
232
+        .join('raids as r', 's.raidid','=','r.id')
233
+        .where('r.id','=',raid.id)
234
+        .join('characters as c', 's.characterid','=','c.id')
235
+        .join('tokens as t', 't.characterid','=','c.id')
236
+        .join('items as i', 'i.id','=','t.itemid')
237
+
238
+        tokenData.forEach(data => {
239
+            if(!ret.tokens[data.itemname])
240
+                ret.tokens[data.itemname] = []
241
+            ret.tokens[data.itemname].push(data)
242
+        })
243
+        return {
244
+            ...raidInDb,
245
+            ...ret
246
+        }
247
+    }
93
     
248
     
94
     getSignups = async (raid:Raid) : Promise<Signup[]> => await this.admin
249
     getSignups = async (raid:Raid) : Promise<Signup[]> => await this.admin
95
     .knex('signups')
250
     .knex('signups')
99
     .select('*')
254
     .select('*')
100
     .where('raidid', '=', raid.id!)
255
     .where('raidid', '=', raid.id!)
101
     
256
     
102
-    sign = async (usertoken:string, character:Character, raid:Raid) => {
103
-        const maybeUserRecord = this.login.getUserRecordByToken(usertoken)
257
+    sign = async (usertoken:string, character:Character, raid:Raid, late:boolean) => {
258
+        const maybeUserRecord = this.userManager.getUserRecordByToken(usertoken)
104
         if(!maybeUserRecord || maybeUserRecord.user.id != character.userid){
259
         if(!maybeUserRecord || maybeUserRecord.user.id != character.userid){
105
             throw new Error("Bad Usertoken")
260
             throw new Error("Bad Usertoken")
106
         }
261
         }
107
 
262
 
108
-        await this.admin
263
+        const exists = await this.admin
109
         .knex('signups')
264
         .knex('signups')
110
-        .insert({
265
+        .select('*')
266
+        .where({
267
+            raidid: raid.id!,
268
+            characterid: character.id!,
269
+        })
270
+        .first()
271
+
272
+        if(!exists){
273
+            await this.admin
274
+            .knex('signups')
275
+            .insert({
276
+                raidid: raid.id!,
277
+                characterid: character.id!,
278
+                late: late
279
+            })
280
+        }else{
281
+            await this.admin
282
+            .knex('signups')
283
+            .update({
284
+                raidid: raid.id!,
285
+                characterid: character.id!,
286
+                late: late
287
+            })
288
+        }
289
+    }
290
+
291
+    unsign = async (usertoken:string, character:Character, raid:Raid) => {
292
+        const maybeUserRecord = this.userManager.getUserRecordByToken(usertoken)
293
+        if(!maybeUserRecord || maybeUserRecord.user.id != character.userid){
294
+            throw new Error("Bad Usertoken")
295
+        }
296
+
297
+        await this.admin.knex('signups')
298
+        .where({
111
             raidid: raid.id!,
299
             raidid: raid.id!,
112
-            characterid: character.id!
300
+            characterid: character.id!,
301
+        })
302
+        .del()
303
+    }
304
+
305
+    setBenched = async (signup: Signup) : Promise<void> => {
306
+        await this.admin.knex('signups')
307
+        .where({
308
+            raidid: signup.raidid,
309
+            characterid: signup.characterid
113
         })
310
         })
311
+        .update(signup)
114
     }
312
     }
115
 }
313
 }

+ 14
- 0
src/backend/Components/Shoutbox/Interface.ts View File

1
+import { Raid, Signup, Character, RaidData } from "../../Types/Types"
2
+import { SubscriptionResponse } from "rpclibrary"
3
+
4
+export type ShoutMessage = {
5
+    message: string,
6
+    sender: string,
7
+    date: string
8
+}
9
+
10
+export class IShoutbox{
11
+    shout: (uuid:string, msg: ShoutMessage) => Promise<void>
12
+    getFeed: () => Promise<ShoutMessage[]>
13
+    subscribe: (callback) => Promise<SubscriptionResponse>
14
+}

+ 13
- 0
src/backend/Components/Shoutbox/RPCInterface.ts View File

1
+import { IShoutbox } from "./Interface"
2
+
3
+export type ShoutboxIfc = {
4
+    Shoutbox:{
5
+        getFeed: IShoutbox['getFeed']
6
+        shout: IShoutbox['shout']
7
+        subscribe: IShoutbox['subscribe']
8
+    }
9
+}
10
+
11
+export type ShoutboxFeatureIfc = {
12
+    
13
+}

+ 64
- 0
src/backend/Components/Shoutbox/Shoutbox.ts View File

1
+import { Injectable } from "../../Injector/ServiceDecorator";
2
+import { ShoutboxFeatureIfc, ShoutboxIfc } from "./RPCInterface";
3
+import { FrontworkComponent } from "../../Types/FrontworkComponent";
4
+import { TableDefiniton } from "../../Types/Types";
5
+import { IShoutbox, ShoutMessage } from "./Interface";
6
+import { SubscriptionResponse, makeSubResponse } from "rpclibrary";
7
+import * as  CircularBuffer from "circular-buffer";
8
+
9
+@Injectable(IShoutbox)
10
+export class Shoutbox
11
+implements FrontworkComponent<ShoutboxIfc, ShoutboxFeatureIfc>, IShoutbox{
12
+    name = "Shoutbox" as "Shoutbox";    
13
+
14
+    log: CircularBuffer = new CircularBuffer(500)
15
+    subs = {}
16
+
17
+    exportRPCs = () => [
18
+        this.shout,
19
+        this.getFeed,
20
+        {
21
+            name: 'subscribe' as 'subscribe',
22
+            hook: this.subscribe,
23
+            onClose: (subres, rpcName) => {
24
+                this.unsubscribe(subres.uuid)
25
+            }
26
+        }
27
+    ]
28
+
29
+    exportRPCFeatures = () => []
30
+    
31
+    getTableDefinitions(): TableDefiniton[] {
32
+        return []
33
+    }
34
+
35
+    shout = async (uuid:string, msg: ShoutMessage) : Promise<void> => {
36
+        if(!this.subs[uuid]) return
37
+        this.broadcast(uuid, msg)
38
+        this.log.push(msg)
39
+    }
40
+
41
+    private broadcast = (uuid:string, msg: ShoutMessage) => {
42
+        Object.values(this.subs).forEach((cb:any) => {
43
+            try{
44
+                cb(msg)
45
+            }catch(e){
46
+                this.unsubscribe(uuid)
47
+            }
48
+        })
49
+    }
50
+
51
+    getFeed = async () : Promise<ShoutMessage[]> => {
52
+        return this.log.toarray()
53
+    }
54
+
55
+    subscribe = async (callback) : Promise<SubscriptionResponse> => {
56
+        const resp = makeSubResponse({})
57
+        this.subs[resp.uuid] = callback
58
+        return resp
59
+    }
60
+
61
+    unsubscribe = async (uuid: string) => {
62
+        delete this.subs[uuid]
63
+    }
64
+}

src/backend/Components/Login/Interface.ts → src/backend/Components/User/Interface.ts View File

1
 import { Auth, Rank, User, RPCPermission, UserRecord } from "../../Types/Types"
1
 import { Auth, Rank, User, RPCPermission, UserRecord } from "../../Types/Types"
2
 
2
 
3
-export class ILoginManager{
3
+export class IUserManager{
4
     login: (username:string, pwHash:string) => Promise<Auth>
4
     login: (username:string, pwHash:string) => Promise<Auth>
5
     logout: (username: string, tokenValue :string) => Promise<void>
5
     logout: (username: string, tokenValue :string) => Promise<void>
6
     getAuth: (tokenValue: string) => Promise<Auth | void>
6
     getAuth: (tokenValue: string) => Promise<Auth | void>
9
     getPermissions: () => Promise<RPCPermission[]>
9
     getPermissions: () => Promise<RPCPermission[]>
10
     checkToken: (token: string, rank: Rank) => boolean
10
     checkToken: (token: string, rank: Rank) => boolean
11
     getUserRecordByToken: (tokenValue: string) => UserRecord | void
11
     getUserRecordByToken: (tokenValue: string) => UserRecord | void
12
+    getUser: (username: string) => Promise<User | void>
13
+
14
+    decrementCurrency: (user: User, value: number) => Promise<void>
15
+    incrementCurrency: (user: User, value: number) => Promise<void>
16
+    setCurrency: (user: User, value: number) => Promise<void>
17
+    getCurrency: (user:User) => Promise<number>
18
+    changeRank: (user:User, rank: Rank) => Promise<User>
19
+    wipeCurrency: () => Promise<void>
12
 }
20
 }

+ 26
- 0
src/backend/Components/User/RPCInterface.ts View File

1
+import { IUserManager } from "./Interface"
2
+
3
+export type UserManagerIfc = {
4
+    UserManager: {
5
+        checkToken: IUserManager['checkToken']
6
+        login: IUserManager['login']
7
+        logout: IUserManager['logout']
8
+        createUser: IUserManager['createUser']
9
+        getAuth: IUserManager['getAuth']
10
+        getUser: IUserManager['getUser']
11
+    }
12
+}
13
+
14
+export type UserManagerFeatureIfc = {
15
+    modifyPermissions: {
16
+        setPermission: IUserManager['setPermission']
17
+        getPermissions: IUserManager['getPermissions']
18
+        changeRank: IUserManager['changeRank']
19
+    }
20
+
21
+    softreserveCurrency: {
22
+        incrementCurrency: IUserManager['incrementCurrency']
23
+        decrementCurrency: IUserManager['decrementCurrency']
24
+        setCurrency: IUserManager['setCurrency']
25
+    }
26
+}

src/backend/Components/Login/LoginManager.ts → src/backend/Components/User/UserManager.ts View File

1
 import { RPCServer, Socket } from "rpclibrary";
1
 import { RPCServer, Socket } from "rpclibrary";
2
-import { Inject, Module } from "../../Injector/ServiceDecorator";
2
+import { Inject, Injectable } from "../../Injector/ServiceDecorator";
3
 import { FrontworkAdmin } from "../../Admin/Admin";
3
 import { FrontworkAdmin } from "../../Admin/Admin";
4
 import { GuildManager } from "../Guild/GuildManager";
4
 import { GuildManager } from "../Guild/GuildManager";
5
 import { ItemManager } from "../Item/ItemManager";
5
 import { ItemManager } from "../Item/ItemManager";
6
 import { RaidManager } from "../Raid/RaidManager";
6
 import { RaidManager } from "../Raid/RaidManager";
7
 import { CharacterManager } from "../Character/CharacterManager";
7
 import { CharacterManager } from "../Character/CharacterManager";
8
-import { LoginManagerIfc, LoginManagerFeatureIfc } from "./RPCInterface";
8
+import { UserManagerFeatureIfc, UserManagerIfc } from "./RPCInterface";
9
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
9
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
10
 import { Rank, User, Auth, _Rank, TableDefiniton, RPCPermission, FrontcraftFeatureIfc, AnyRPCExporter, Token, UserRecord } from "../../Types/Types";
10
 import { Rank, User, Auth, _Rank, TableDefiniton, RPCPermission, FrontcraftFeatureIfc, AnyRPCExporter, Token, UserRecord } from "../../Types/Types";
11
 import { IAdmin } from "../../Admin/Interface";
11
 import { IAdmin } from "../../Admin/Interface";
12
-import { ILoginManager } from "./Interface";
12
+import { IUserManager } from "./Interface";
13
 import { getLogger, Logger } from "log4js";
13
 import { getLogger, Logger } from "log4js";
14
+import { saltedHash } from "../../Util/hash";
14
 
15
 
15
 const uuid = require('uuid/v4')
16
 const uuid = require('uuid/v4')
16
 
17
 
18
+const salt = "6pIbc6yjSN"
17
 const ONE_WEEK = 604800000 
19
 const ONE_WEEK = 604800000 
18
 
20
 
19
 type Serverstate = {
21
 type Serverstate = {
20
     server: RPCServer,
22
     server: RPCServer,
21
     port : number,
23
     port : number,
22
     allowed: string[]
24
     allowed: string[]
23
-}
25
+};
24
 
26
 
25
 
27
 
26
-@Module(ILoginManager)
27
-export class LoginManager
28
-implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginManager{
29
-    name = "Authenticator" as "Authenticator";   
28
+@Injectable(IUserManager)
29
+export class UserManager
30
+implements FrontworkComponent<UserManagerIfc, UserManagerFeatureIfc>, IUserManager{
31
+    name = "UserManager" as "UserManager"
30
 
32
 
31
     @Inject(IAdmin)
33
     @Inject(IAdmin)
32
     private admin: FrontworkAdmin
34
     private admin: FrontworkAdmin
48
     userLogins : {[username in string] : UserRecord} = {}
50
     userLogins : {[username in string] : UserRecord} = {}
49
 
51
 
50
     exportRPCs = () => [
52
     exportRPCs = () => [
51
-        {
52
-            name: 'login' as 'login',
53
-            call: this.login 
54
-        },{
55
-            name: 'logout' as 'logout',
56
-            call: this.logout
57
-        },{
58
-            name: 'getAuth' as 'getAuth',
59
-            call: this.getAuth
60
-        },{
61
-            name: 'checkToken' as 'checkToken',
62
-            call: this.checkToken
63
-        },{
64
-            name: 'createUser' as 'createUser',
65
-            call: this.createUser
66
-        }
53
+        this.login,
54
+        this.logout,
55
+        this.getAuth,
56
+        this.checkToken,
57
+        this.createUser,
58
+        this.getUser
67
     ]
59
     ]
68
 
60
 
69
-    exportRPCFeatures = () => [
70
-        {
71
-            name: 'modifyPermissions' as 'modifyPermissions',
72
-            exportRPCs: () => [{
73
-                name: 'getPermissions' as 'getPermissions',
74
-                call: this.getPermissions 
75
-            },{
76
-                name: 'setPermission' as 'setPermission',
77
-                call: this.setPermission
78
-            }]
79
-        }
80
-    ]
81
-    
61
+    exportRPCFeatures = () => [{
62
+        name: 'modifyPermissions' as 'modifyPermissions',
63
+        exportRPCs: () => [
64
+            this.getPermissions,
65
+            this.setPermission
66
+        ]
67
+    },{
68
+        name: 'softreserveCurrency' as 'softreserveCurrency',
69
+        exportRPCs: () => [
70
+            this.incrementCurrency,
71
+            this.decrementCurrency,
72
+            this.setCurrency
73
+        ]
74
+    }]
75
+
76
+    changeRank = async (user:User, rank:Rank): Promise<User> => {
77
+        await this.admin
78
+        .knex('users')
79
+        .where({
80
+            user:user.username
81
+        }).update({
82
+            rank: rank
83
+        })
84
+
85
+        return await this.admin
86
+        .knex('users')
87
+        .where({
88
+            user: user.username
89
+        }).first()
90
+    }
82
     
91
     
83
     getTableDefinitions = (): TableDefiniton[] => [
92
     getTableDefinitions = (): TableDefiniton[] => [
84
         {
93
         {
89
                 table.string("pwhash").notNullable()
98
                 table.string("pwhash").notNullable()
90
                 table.string("rank").notNullable()
99
                 table.string("rank").notNullable()
91
                 table.string("email").nullable().unique()
100
                 table.string("email").nullable().unique()
92
-                table.boolean("locked").defaultTo(true)
101
+                table.integer("currency").defaultTo(1)
93
             }
102
             }
94
         },{
103
         },{
95
             name: 'rpcpermissions',
104
             name: 'rpcpermissions',
109
     initialize = async () => {
118
     initialize = async () => {
110
         this.exporters = [this.guild, this.item, this.raid, this.character] 
119
         this.exporters = [this.guild, this.item, this.raid, this.character] 
111
         //set up permissions
120
         //set up permissions
112
-        getLogger('LoginManager').debug('inserting permissions')
121
+        getLogger('UserManager').debug('inserting permissions')
113
 
122
 
114
         await Promise.all( 
123
         await Promise.all( 
115
             [this, ...this.exporters].flatMap(exp => exp.exportRPCFeatures().map(async (feature) => {
124
             [this, ...this.exporters].flatMap(exp => exp.exportRPCFeatures().map(async (feature) => {
116
             try{
125
             try{
117
                 await this.admin.knex.insert({ rpcname: feature.name }).into('rpcpermissions')
126
                 await this.admin.knex.insert({ rpcname: feature.name }).into('rpcpermissions')
118
             }catch(e){
127
             }catch(e){
119
-                console.log(e);
128
+                getLogger('UserManager').debug(feature.name);
120
             }
129
             }
121
         })))
130
         })))
122
 
131
 
123
         //start rankServers
132
         //start rankServers
124
-        getLogger('LoginManager').debug('Starting rank servers')
133
+        getLogger('UserManager').debug('Starting rank servers')
125
 
134
 
126
         let rankServers = { } as any
135
         let rankServers = { } as any
127
         await Promise.all(_Rank.map(async (r,i) => {
136
         await Promise.all(_Rank.map(async (r,i) => {
148
                 try{
157
                 try{
149
                     //return await state.server.destroy()
158
                     //return await state.server.destroy()
150
                 }catch(e){
159
                 }catch(e){
151
-                    getLogger('LoginManager').warn(e)
160
+                    getLogger('UserManager').warn(e)
152
                 }
161
                 }
153
             })
162
             })
154
         );
163
         );
171
         while(!data){
180
         while(!data){
172
             tries ++
181
             tries ++
173
             if(tries === 5){
182
             if(tries === 5){
174
-                getLogger('LoginManager').debug('Connection check failed for connection *'+socket.port)
183
+                getLogger('UserManager').debug('Connection check failed for connection *'+socket.port)
175
                 socket.destroy()
184
                 socket.destroy()
176
                 return false
185
                 return false
177
             }
186
             }
189
 
198
 
190
     setPermission = async (permission: RPCPermission) => {
199
     setPermission = async (permission: RPCPermission) => {
191
         await this.admin.knex('rpcpermissions')
200
         await this.admin.knex('rpcpermissions')
192
-        .where('rpcname', '=', permission.rpcnamename)
201
+        .where('rpcname', '=', permission.rpcname)
193
         .update(permission)
202
         .update(permission)
194
     }
203
     }
195
 
204
 
198
     }
207
     }
199
 
208
 
200
     getPermission = async (feature: keyof FrontcraftFeatureIfc, rank:Rank) : Promise<boolean> => {
209
     getPermission = async (feature: keyof FrontcraftFeatureIfc, rank:Rank) : Promise<boolean> => {
201
-        const perm : RPCPermission[] = await this.admin.knex
210
+        const perm : RPCPermission = await this.admin.knex
202
             .select(rank)
211
             .select(rank)
203
             .from('rpcpermissions')
212
             .from('rpcpermissions')
204
             .where('rpcname', '=', <string>feature)
213
             .where('rpcname', '=', <string>feature)
214
+            .first()
205
 
215
 
206
-        if(perm.length === 0) return false
207
-        return perm[0][rank]
216
+        if(!perm) return false
217
+        return perm[rank]
208
     }
218
     }
209
 
219
 
210
     getRPCForRank = async (rank: Rank): Promise<AnyRPCExporter[]> => {
220
     getRPCForRank = async (rank: Rank): Promise<AnyRPCExporter[]> => {
231
             if(admins.length > 0){
241
             if(admins.length > 0){
232
                 return {} as User
242
                 return {} as User
233
             }
243
             }
234
-
235
-            user.locked = false
236
         }
244
         }
237
         user.username = user.username.toLowerCase()
245
         user.username = user.username.toLowerCase()
246
+        user.pwhash = await saltedHash(user.pwhash, salt)
238
 
247
 
239
         await this.admin.knex('users')
248
         await this.admin.knex('users')
240
         .insert(user)
249
         .insert(user)
241
 
250
 
242
-        const users = await this.admin.knex
251
+        const userRecord = await this.admin.knex
243
         .select("*")
252
         .select("*")
244
         .from('users')
253
         .from('users')
245
         .where(user)
254
         .where(user)
255
+        .first()
246
 
256
 
247
-        return users[0]
257
+        return userRecord
248
     }
258
     }
249
 
259
 
260
+    getUser = async (username: string) : Promise<User | void> => await this.admin
261
+    .knex('users')
262
+    .select('*')
263
+    .where({
264
+        username: username.toLowerCase()
265
+    })
266
+    .first()
267
+
250
     logout = async (username:string, tokenValue : string) : Promise<void> => {
268
     logout = async (username:string, tokenValue : string) : Promise<void> => {
251
         try{
269
         try{
252
-            username = username.toLowerCase()
253
-            const maybeRecord = this.getUserRecordByToken(tokenValue)
254
-            if(maybeRecord && maybeRecord.auth.user.username != username){
255
-                getLogger('LoginManager').warn(`Bad logout attempt
256
-                token by: ${maybeRecord.auth.user.username}
257
-                tried to logout: ${username}`)
258
-                return
259
-            } 
270
+            if(!this.checkTokenOwnedByUser(username, tokenValue)) return
260
 
271
 
261
             if(this.userLogins[username]){
272
             if(this.userLogins[username]){
262
                 await Promise.all (Object.values(this.userLogins[username].connections).map(async (sock) => {
273
                 await Promise.all (Object.values(this.userLogins[username].connections).map(async (sock) => {
275
         }
286
         }
276
     }
287
     }
277
 
288
 
289
+    wipeCurrency = async () => {
290
+        await this.admin.knex('users')
291
+        .update({currency: 0})
292
+    }
293
+
278
     login = async(username:string, pwHash:string) : Promise<Auth> => {
294
     login = async(username:string, pwHash:string) : Promise<Auth> => {
279
         username = username.toLowerCase()
295
         username = username.toLowerCase()
280
-        const res:User[] = await this.admin.knex
296
+        const user:User = await this.admin.knex
281
         .select('*')
297
         .select('*')
282
         .from('users')
298
         .from('users')
283
         .where({ username: username })
299
         .where({ username: username })
300
+        .first()
301
+         
302
+        const salted = await saltedHash(pwHash, salt)
284
 
303
 
285
-        if(res.length > 0 && pwHash === res[0].pwhash){
286
-            const user:User = res[0]
304
+        if(user && salted === user.pwhash){
287
             delete user.pwhash
305
             delete user.pwhash
288
 
306
 
289
             //return existing auth
307
             //return existing auth
339
                                 }
357
                                 }
340
                             }).catch((e) => {
358
                             }).catch((e) => {
341
                                 socket.destroy();
359
                                 socket.destroy();
342
-                                getLogger('LoginManager').warn(e);
360
+                                getLogger('UserManager').warn(e);
343
                             })
361
                             })
344
                         },
362
                         },
345
                         errorHandler: (socket, e, rpcName, args) => {
363
                         errorHandler: (socket, e, rpcName, args) => {
346
-                            console.log(rpcName, args);
364
+                            getLogger('UserManager').error(rpcName, args, e);
347
                         },
365
                         },
348
                         sesame: (sesame) => this.checkToken(sesame, rank)
366
                         sesame: (sesame) => this.checkToken(sesame, rank)
349
                     })
367
                     })
352
                 new Promise((res, rej) => setTimeout(res, 500))
370
                 new Promise((res, rej) => setTimeout(res, 500))
353
             ])
371
             ])
354
             if(!rpcServer && n>1) 
372
             if(!rpcServer && n>1) 
355
-                getLogger('LoginManager').warn("createServer retry nr.", n, 'port', port)
373
+                getLogger('UserManager').warn("createServer retry nr.", n, 'port', port)
356
         }
374
         }
357
         return rpcServer
375
         return rpcServer
358
     }
376
     }
360
     checkToken = (token: string, rank: Rank) : boolean => this.rankServers[rank].allowed.includes(token)
378
     checkToken = (token: string, rank: Rank) : boolean => this.rankServers[rank].allowed.includes(token)
361
                                                        && Object.values(this.userLogins).find(login => login.auth.token.value === token)!.auth.token.created > Date.now() - ONE_WEEK
379
                                                        && Object.values(this.userLogins).find(login => login.auth.token.value === token)!.auth.token.created > Date.now() - ONE_WEEK
362
 
380
 
381
+    checkTokenOwnedByUser = (username: string, tokenValue: string) => {
382
+        username = username.toLowerCase()
383
+        const maybeRecord = this.getUserRecordByToken(tokenValue)
384
+        if(!maybeRecord || maybeRecord.auth.user.username != username){
385
+            getLogger('UserManager').warn(`Bad logout attempt
386
+            token by: ${maybeRecord?maybeRecord.auth.user.username:tokenValue}
387
+            tried to logout: ${username}`)
388
+            return false
389
+        } 
390
+        return true
391
+    }
392
+                                                       
363
     createToken = (user:User): Token => {
393
     createToken = (user:User): Token => {
364
         
394
         
365
         if(this.userLogins[user.username]){
395
         if(this.userLogins[user.username]){
374
 
404
 
375
         return token
405
         return token
376
     }
406
     }
407
+
408
+    getCurrency = async(user:User) : Promise<number> => { 
409
+        const usr : User = await this.admin
410
+        .knex('users')
411
+        .where('username', '=', user.username)
412
+        .select('*')
413
+        .first()
414
+
415
+        return usr.currency!
416
+    }
417
+
418
+    decrementCurrency = async (user: User, value = 1) => {
419
+        if(value < 1) return
420
+
421
+        const usr : User = await this.admin
422
+        .knex('users')
423
+        .where('id', '=', user.id)
424
+        .select('*')
425
+        .first()
426
+
427
+        if(!usr || usr.currency! <= 0) return
428
+
429
+        await this.admin
430
+        .knex('users')
431
+        .where('id', '=', user.id)
432
+        .decrement('currency', value)
433
+    }
434
+
435
+    incrementCurrency = async (user: User, value = 1) => {
436
+        if(value < 1) return
437
+        await this.admin
438
+        .knex('users')
439
+        .where('id', '=', user.id)
440
+        .increment('currency', value)
441
+    }
442
+
443
+    setCurrency = async (user: User, value: number) => {
444
+        if(value < 0) return
445
+        await this.admin
446
+        .knex('users')
447
+        .where('id', '=', user.id)
448
+        .update('currency', value)
449
+    }
450
+
451
+
377
 }
452
 }

+ 1
- 3
src/backend/Injector/Injector.ts View File

51
       return rootobj
51
       return rootobj
52
     }
52
     }
53
     this.moduleObjs[target.name] = new target()
53
     this.moduleObjs[target.name] = new target()
54
-    return this.moduleObjs[target.name] as any
55
-    
56
-    
54
+    return this.moduleObjs[target.name] as any   
57
   }
55
   }
58
 };
56
 };

+ 6
- 6
src/backend/Injector/ServiceDecorator.ts View File

6
  * @returns {GenericClassDecorator<Type<any>>}
6
  * @returns {GenericClassDecorator<Type<any>>}
7
  * @constructor
7
  * @constructor
8
  */
8
  */
9
-export const Module = (ifc?: Type<any>) : GenericClassDecorator<Type<any>> => {
9
+export const Injectable = (_interface?: Type<any>) : GenericClassDecorator<Type<any>> => {
10
   return (target: Type<any>) => {
10
   return (target: Type<any>) => {
11
     Injector.modules.push({
11
     Injector.modules.push({
12
-      implements: ifc,
12
+      implements: _interface,
13
       implementation: target
13
       implementation: target
14
     })
14
     })
15
   }
15
   }
20
  * @constructor
20
  * @constructor
21
  */
21
  */
22
 export const RootComponent = (config : {
22
 export const RootComponent = (config : {
23
-    implements : Type<any>
24
-    imports : Type<FrontworkComponent>[]
23
+    injectable : Type<any>
24
+    injects : Type<FrontworkComponent>[]
25
   }) : GenericClassDecorator<Type<any>> => {
25
   }) : GenericClassDecorator<Type<any>> => {
26
   return (target: Type<any>) => {
26
   return (target: Type<any>) => {
27
-    Injector.rootModules = config.imports
28
-    Injector.rootInterface = config.implements
27
+    Injector.rootModules = config.injects
28
+    Injector.rootInterface = config.injectable
29
     Injector.root = target
29
     Injector.root = target
30
   }
30
   }
31
 }
31
 }

+ 46
- 7
src/backend/Types/Types.ts View File

1
 import * as Knex from "knex"
1
 import * as Knex from "knex"
2
 import { RPCExporter, Socket } from "rpclibrary";
2
 import { RPCExporter, Socket } from "rpclibrary";
3
 import { RaidManagerIfc, RaidManagerFeatureIfc } from "../Components/Raid/RPCInterface";
3
 import { RaidManagerIfc, RaidManagerFeatureIfc } from "../Components/Raid/RPCInterface";
4
-import { LoginManagerIfc, LoginManagerFeatureIfc } from "../Components/Login/RPCInterface";
4
+import { UserManagerIfc, UserManagerFeatureIfc } from "../Components/User/RPCInterface";
5
 import { CharacterManagerIfc, CharacterManagerFeatureIfc } from "../Components/Character/RPCInterface";
5
 import { CharacterManagerIfc, CharacterManagerFeatureIfc } from "../Components/Character/RPCInterface";
6
+import { ItemManagerFeatureIfc, ItemManagerIfc } from "../Components/Item/RPCInterface";
7
+import { GuildManagerFeatureIfc, GuildManagerIfc } from "../Components/Guild/RPCInterface";
8
+import { ShoutboxIfc } from "../Components/Shoutbox/RPCInterface";
6
 
9
 
7
 export type FrontcraftIfc = RaidManagerIfc
10
 export type FrontcraftIfc = RaidManagerIfc
8
-                          & LoginManagerIfc
11
+                          & UserManagerIfc
9
                           & CharacterManagerIfc 
12
                           & CharacterManagerIfc 
13
+                          & ItemManagerIfc
14
+                          & GuildManagerIfc
15
+                          & ShoutboxIfc
10
 
16
 
11
 export type FrontcraftFeatureIfc = RaidManagerFeatureIfc 
17
 export type FrontcraftFeatureIfc = RaidManagerFeatureIfc 
12
-                                 & LoginManagerFeatureIfc
18
+                                 & UserManagerFeatureIfc
13
                                  & CharacterManagerFeatureIfc
19
                                  & CharacterManagerFeatureIfc
20
+                                 & ItemManagerFeatureIfc
21
+                                 & GuildManagerFeatureIfc
14
 
22
 
15
 export declare type NotificationSeverity = 'Info' | 'Important' | 'Error';
23
 export declare type NotificationSeverity = 'Info' | 'Important' | 'Error';
16
 
24
 
25
     tableBuilder: (table: Knex.CreateTableBuilder) => void
33
     tableBuilder: (table: Knex.CreateTableBuilder) => void
26
 }
34
 }
27
 
35
 
36
+export type Race = "Human" | "Gnome" | "Night Elf" | "Dwarf"
28
 export type Rank =             "ADMIN" | "Guildmaster" | "Officer" | "Classleader" | "Raider" | "Trial" | "Social" | "Guest"
37
 export type Rank =             "ADMIN" | "Guildmaster" | "Officer" | "Classleader" | "Raider" | "Trial" | "Social" | "Guest"
29
 export const _Rank : Rank[] = ["ADMIN" , "Guildmaster" , "Officer" , "Classleader" , "Raider" , "Trial" , "Social" , "Guest"]
38
 export const _Rank : Rank[] = ["ADMIN" , "Guildmaster" , "Officer" , "Classleader" , "Raider" , "Trial" , "Social" , "Guest"]
30
 export type Class =              "Warrior" | "Rogue" | "Hunter" | "Mage" | "Warlock" | "Priest" | "Shaman" | "Paladin" | "Druid"
39
 export type Class =              "Warrior" | "Rogue" | "Hunter" | "Mage" | "Warlock" | "Priest" | "Shaman" | "Paladin" | "Druid"
33
 export type AnyRPCExporter = RPCExporter<any,any>
42
 export type AnyRPCExporter = RPCExporter<any,any>
34
 
43
 
35
 export type RPCPermission = {
44
 export type RPCPermission = {
36
-    rpcnamename: string
45
+    rpcname: string
37
 } & {
46
 } & {
38
     [rank in Rank] : boolean
47
     [rank in Rank] : boolean
39
 }
48
 }
40
 
49
 
50
+export type RaidData = Raid & {
51
+    participants: {
52
+        [clazz in Class] : (Character & Spec)[] 
53
+    } & {
54
+        late: (Character & Spec)[]
55
+        bench: (Character & Spec)[] 
56
+    }
57
+    tokens: {
58
+        [itemname in string]: (Character & SRToken & Item)[]
59
+    }
60
+}
61
+
62
+export type SRToken = {
63
+    characterid: number,
64
+    itemid: number,
65
+    level: number
66
+}
67
+
68
+export type SRPriority = {
69
+    id?:number
70
+    race?:Race
71
+    specid?:number,
72
+    itemid?:number,
73
+    description?:string,
74
+    modifier:number
75
+}
76
+
41
 export type Item = {
77
 export type Item = {
42
     id?:number
78
     id?:number
43
     itemname:string
79
     itemname:string
52
     username: string
88
     username: string
53
     pwhash: string
89
     pwhash: string
54
     rank: Rank
90
     rank: Rank
55
-    email?: string
56
-    locked: boolean
91
+    currency?: number
57
 }
92
 }
58
 
93
 
59
 export type Raid = {
94
 export type Raid = {
61
     title: string
96
     title: string
62
     description: string
97
     description: string
63
     start: string
98
     start: string
64
-    minrank: Rank
99
+    signupcount?: number
100
+    size: number
65
 }
101
 }
66
 
102
 
67
 export type Signup = {
103
 export type Signup = {
68
     raidid: number
104
     raidid: number
69
     characterid: number
105
     characterid: number
106
+    benched: boolean
107
+    late: boolean
70
 }
108
 }
71
 
109
 
72
 export type Character = {
110
 export type Character = {
73
     id? : number
111
     id? : number
74
     charactername : string
112
     charactername : string
113
+    race: Race
75
     specid : number
114
     specid : number
76
     userid : number
115
     userid : number
77
 }
116
 }

+ 7
- 0
src/backend/Util/hash.ts View File

1
+const SHA256 = require("crypto-js/sha256");
2
+const Hex = require('crypto-js/enc-hex');
3
+
4
+export async function saltedHash(input: string, salt: string) {
5
+    const hash = await SHA256(input+salt)
6
+    return Hex.stringify(hash)
7
+}

+ 3
- 3
src/frontend/src/app/@core/core.module.ts View File

9
   PlayerService,
9
   PlayerService,
10
   StateService,
10
   StateService,
11
 } from './utils';
11
 } from './utils';
12
-import { LoginApiService, initializeLoginSvc } from '../frontcraft/services/login-api';
12
+import { ApiService, initializeLoginSvc } from '../frontcraft/services/login-api';
13
 
13
 
14
 const DATA_SERVICES = [  
14
 const DATA_SERVICES = [  
15
-  {provide: LoginApiService, useClass: LoginApiService},
15
+  {provide: ApiService, useClass: ApiService},
16
   {provide: CookieService, useClass: CookieService},
16
   {provide: CookieService, useClass: CookieService},
17
   {
17
   {
18
     provide: APP_INITIALIZER,
18
     provide: APP_INITIALIZER,
19
     useFactory: initializeLoginSvc,
19
     useFactory: initializeLoginSvc,
20
-    deps: [LoginApiService, CookieService],
20
+    deps: [ApiService, CookieService],
21
     multi: true
21
     multi: true
22
   }
22
   }
23
 ];
23
 ];

+ 43
- 0
src/frontend/src/app/@theme/components/header/chat.component.ts View File

1
+import { Component } from '@angular/core';
2
+import { ApiService } from '../../../frontcraft/services/login-api';
3
+import { ShoutMessage } from '../../../../../../backend/Components/Shoutbox/Interface';
4
+
5
+@Component({
6
+  selector: 'chat',
7
+  template: `
8
+        <nb-chat title="Shoutbox" size="large" status="primary">
9
+          <nb-chat-message *ngFor="let msg of messages"
10
+                           [type]="text"
11
+                           [message]="msg.message"
12
+                           [sender]="msg.sender"
13
+                           [date]="msg.date">
14
+          </nb-chat-message>
15
+          <nb-chat-form (send)="submit($event)" [dropFiles]="false">
16
+          </nb-chat-form>
17
+        </nb-chat>
18
+  `, 
19
+  styles: [`
20
+  nb-chat {
21
+    width: 600px;
22
+    margin: 0.5rem 0 2rem 2rem;
23
+  }`],
24
+})
25
+export class ChatComponent {
26
+
27
+  messages : ShoutMessage[] = []
28
+  history
29
+
30
+  constructor(
31
+    private api: ApiService
32
+  ) {}
33
+
34
+  submit(event){
35
+    this.sendMessage({
36
+      date: ""+Date.now(),
37
+      message: event.message,
38
+      sender: this.api.getCurrentUser().username
39
+    })
40
+  }
41
+
42
+  sendMessage : (msg:ShoutMessage)=>Promise<void>
43
+}

+ 15
- 4
src/frontend/src/app/@theme/components/header/header.component.html View File

3
     <a (click)="toggleSidebar()" href="#" class="sidebar-toggle">
3
     <a (click)="toggleSidebar()" href="#" class="sidebar-toggle">
4
       <nb-icon icon="menu-2-outline"></nb-icon>
4
       <nb-icon icon="menu-2-outline"></nb-icon>
5
     </a>
5
     </a>
6
-    <a class="logo" href="#" (click)="navigateHome()">Frontcraft</a>
6
+    <a class="logo" href="#" (click)="navigateHome()">{{title}}</a>
7
   </div>
7
   </div>
8
 </div>
8
 </div>
9
 
9
 
10
 <div class="header-container">
10
 <div class="header-container">
11
   <nb-actions size="small">
11
   <nb-actions size="small">
12
 
12
 
13
-    <nb-action class="control-item" icon="message-square-outline"></nb-action>
14
-    <nb-action class="control-item" icon="bell-outline"></nb-action>
15
-
13
+    <nb-action 
14
+      *ngIf="!newmessage"
15
+      icon="message-square-outline" 
16
+      (click)="openChat()">
17
+    </nb-action>
18
+    <nb-action 
19
+      *ngIf="newmessage"
20
+      icon="message-square-outline"
21
+      badgeText="new"
22
+      badgePosition="top right"
23
+      badgeStatus="info"
24
+      (click)="openChat()">
25
+    </nb-action>
26
+  
16
     <nb-action class="user-action">
27
     <nb-action class="user-action">
17
       <nb-user [nbContextMenu]="userMenu"
28
       <nb-user [nbContextMenu]="userMenu"
18
                [onlyPicture]="false"
29
                [onlyPicture]="false"

+ 48
- 22
src/frontend/src/app/@theme/components/header/header.component.ts View File

1
 import { Component, OnDestroy, OnInit } from '@angular/core';
1
 import { Component, OnDestroy, OnInit } from '@angular/core';
2
-import { NbMediaBreakpointsService, NbMenuService, NbSidebarService, NbThemeService, NbContextMenuComponent, NbMenuItem } from '@nebular/theme';
2
+import { NbMediaBreakpointsService, NbSidebarService, NbThemeService, NbMenuItem, NbDialogService } from '@nebular/theme';
3
 
3
 
4
 import { LayoutService } from '../../../@core/utils';
4
 import { LayoutService } from '../../../@core/utils';
5
 import { map, takeUntil } from 'rxjs/operators';
5
 import { map, takeUntil } from 'rxjs/operators';
6
 import { Subject } from 'rxjs';
6
 import { Subject } from 'rxjs';
7
-import { LoginApiService } from '../../../frontcraft/services/login-api';
7
+import { ApiService } from '../../../frontcraft/services/login-api';
8
 import { User } from '../../../../../../backend/Types/Types';
8
 import { User } from '../../../../../../backend/Types/Types';
9
+import { Router } from '@angular/router';
10
+import { ChatComponent } from './chat.component';
11
+import { ShoutMessage } from '../../../../../../backend/Components/Shoutbox/Interface';
9
 
12
 
10
 @Component({
13
 @Component({
11
   selector: 'ngx-header',
14
   selector: 'ngx-header',
15
 export class HeaderComponent implements OnInit, OnDestroy {
18
 export class HeaderComponent implements OnInit, OnDestroy {
16
 
19
 
17
   private destroy$: Subject<void> = new Subject<void>();
20
   private destroy$: Subject<void> = new Subject<void>();
18
-  userPictureOnly: boolean = false;
19
   user: User;
21
   user: User;
22
+  title = "Loading ... ";
20
 
23
 
21
   themes = [
24
   themes = [
22
     {
25
     {
38
   ];
41
   ];
39
 
42
 
40
   currentTheme = 'dark';
43
   currentTheme = 'dark';
41
-
42
   userMenu : NbMenuItem[] = [ { title: 'Log out', link: '/auth/logout' } ];
44
   userMenu : NbMenuItem[] = [ { title: 'Log out', link: '/auth/logout' } ];
43
 
45
 
46
+  sendMessage: any = console.log
47
+  chatwindow: ChatComponent
48
+  chatlog: ShoutMessage[] = []
49
+  newmessage = false
50
+  lastmessage = "asdasd"
51
+
44
   constructor(private sidebarService: NbSidebarService,
52
   constructor(private sidebarService: NbSidebarService,
45
-              private menuService: NbMenuService,
53
+              private router: Router,
46
               private themeService: NbThemeService,
54
               private themeService: NbThemeService,
47
               private layoutService: LayoutService,
55
               private layoutService: LayoutService,
48
               private breakpointService: NbMediaBreakpointsService,
56
               private breakpointService: NbMediaBreakpointsService,
49
-              private loginService: LoginApiService,
57
+              private api: ApiService,
58
+              private dialogService : NbDialogService
50
              ) {}
59
              ) {}
51
 
60
 
52
   ngOnInit() {
61
   ngOnInit() {
53
     this.currentTheme = this.themeService.currentTheme;
62
     this.currentTheme = this.themeService.currentTheme;
54
 
63
 
55
-    this.user = this.loginService.getCurrentUser()
64
+    this.user = this.api.getCurrentUser()
56
     if(this.user)
65
     if(this.user)
57
       this.userMenu.unshift({ title: 'Profile', link: '/frontcraft/user/'+this.user.username });
66
       this.userMenu.unshift({ title: 'Profile', link: '/frontcraft/user/'+this.user.username });
58
-
59
-    /*
60
-    this.userService.getUsers()
61
-      .pipe(takeUntil(this.destroy$))
62
-      .subscribe((users: any) => this.user = users.lee);
63
-    */
64
    
67
    
65
-    const { xl } = this.breakpointService.getBreakpointsMap();
66
-    this.themeService.onMediaQueryChange()
67
-      .pipe(
68
-        map(([, currentBreakpoint]) => currentBreakpoint.width < xl),
69
-        takeUntil(this.destroy$),
70
-      )
71
-      .subscribe((isLessThanXl: boolean) => this.userPictureOnly = isLessThanXl);
68
+    this.api.get('GuildManager').getGuildInfo().then(info => {
69
+      this.title = info.name
70
+    })
71
+
72
+    this.api.connectShoutbox((msg) => {
73
+      this.chatlog.push(msg)
74
+      if(msg.message != this.lastmessage)
75
+        this.newmessage = true
76
+      if(this.chatwindow) this.chatwindow.messages.push(msg)
77
+    }).then(sendMsg => {
78
+      this.sendMessage = (msg) => {
79
+        sendMsg(msg)
80
+        this.lastmessage = msg.message
81
+      }
82
+      this.api.get('Shoutbox').getFeed().then(log => {this.chatlog = log})
83
+    })
72
 
84
 
73
     this.themeService.onThemeChange()
85
     this.themeService.onThemeChange()
74
       .pipe(
86
       .pipe(
78
       .subscribe(themeName => this.currentTheme = themeName);
90
       .subscribe(themeName => this.currentTheme = themeName);
79
   }
91
   }
80
 
92
 
93
+  openChat(){
94
+    this.newmessage = false
95
+    const ref = this.dialogService.open(ChatComponent, {
96
+      context: {
97
+        sendMessage: this.sendMessage,
98
+      }
99
+    });
100
+    ref.onClose.subscribe(()=>{
101
+      this.chatwindow = null
102
+    })
103
+    this.chatwindow = ref.componentRef.instance
104
+    this.chatwindow.messages.push(...this.chatlog)
105
+  }
106
+
81
   ngOnDestroy() {
107
   ngOnDestroy() {
82
     this.destroy$.next();
108
     this.destroy$.next();
83
     this.destroy$.complete();
109
     this.destroy$.complete();
95
   }
121
   }
96
 
122
 
97
   logout() {
123
   logout() {
98
-    this.loginService.logout()
124
+    this.api.logout()
99
   }
125
   }
100
 
126
 
101
   navigateHome() {
127
   navigateHome() {
102
-    this.menuService.navigateHome();
128
+    this.router.navigateByUrl('/')
103
     return false;
129
     return false;
104
   }
130
   }
105
 }
131
 }

+ 12
- 1
src/frontend/src/app/@theme/theme.module.ts View File

12
   NbSelectModule,
12
   NbSelectModule,
13
   NbIconModule,
13
   NbIconModule,
14
   NbThemeModule,
14
   NbThemeModule,
15
+  NbDialogModule,
16
+  NbChatModule,
17
+  NbCardModule,
15
 } from '@nebular/theme';
18
 } from '@nebular/theme';
16
 import { NbEvaIconsModule } from '@nebular/eva-icons';
19
 import { NbEvaIconsModule } from '@nebular/eva-icons';
17
 import { NbSecurityModule } from '@nebular/security';
20
 import { NbSecurityModule } from '@nebular/security';
39
 import { COSMIC_THEME } from './styles/theme.cosmic';
42
 import { COSMIC_THEME } from './styles/theme.cosmic';
40
 import { CORPORATE_THEME } from './styles/theme.corporate';
43
 import { CORPORATE_THEME } from './styles/theme.corporate';
41
 import { DARK_THEME } from './styles/theme.dark';
44
 import { DARK_THEME } from './styles/theme.dark';
45
+import { ChatComponent } from './components/header/chat.component';
42
 
46
 
43
 const NB_MODULES = [
47
 const NB_MODULES = [
44
   NbLayoutModule,
48
   NbLayoutModule,
53
   NbSelectModule,
57
   NbSelectModule,
54
   NbIconModule,
58
   NbIconModule,
55
   NbEvaIconsModule,
59
   NbEvaIconsModule,
60
+  NbChatModule,
61
+  NbCardModule,
62
+  NbDialogModule.forChild()
56
 ];
63
 ];
57
 const COMPONENTS = [
64
 const COMPONENTS = [
58
   HeaderComponent,
65
   HeaderComponent,
62
   OneColumnLayoutComponent,
69
   OneColumnLayoutComponent,
63
   ThreeColumnsLayoutComponent,
70
   ThreeColumnsLayoutComponent,
64
   TwoColumnsLayoutComponent,
71
   TwoColumnsLayoutComponent,
65
-  OneColumnNoSidebarLayoutComponent
72
+  OneColumnNoSidebarLayoutComponent,
73
+  ChatComponent
66
 ];
74
 ];
67
 const PIPES = [
75
 const PIPES = [
68
   CapitalizePipe,
76
   CapitalizePipe,
76
   imports: [CommonModule, ...NB_MODULES],
84
   imports: [CommonModule, ...NB_MODULES],
77
   exports: [CommonModule, ...PIPES, ...COMPONENTS],
85
   exports: [CommonModule, ...PIPES, ...COMPONENTS],
78
   declarations: [...COMPONENTS, ...PIPES],
86
   declarations: [...COMPONENTS, ...PIPES],
87
+  entryComponents: [
88
+    ChatComponent
89
+  ]
79
 })
90
 })
80
 export class ThemeModule {
91
 export class ThemeModule {
81
   static forRoot(): ModuleWithProviders {
92
   static forRoot(): ModuleWithProviders {

+ 2
- 2
src/frontend/src/app/app.component.ts View File

5
  */
5
  */
6
 import { Component, OnInit } from '@angular/core';
6
 import { Component, OnInit } from '@angular/core';
7
 import { AnalyticsService } from './@core/utils/analytics.service';
7
 import { AnalyticsService } from './@core/utils/analytics.service';
8
-import { LoginApiService } from './frontcraft/services/login-api';
8
+import { ApiService } from './frontcraft/services/login-api';
9
 
9
 
10
 @Component({
10
 @Component({
11
   selector: 'ngx-app',
11
   selector: 'ngx-app',
13
 })
13
 })
14
 export class AppComponent implements OnInit {
14
 export class AppComponent implements OnInit {
15
 
15
 
16
-  constructor(private loginSvc: LoginApiService, private analytics: AnalyticsService) {
16
+  constructor(private loginSvc: ApiService, private analytics: AnalyticsService) {
17
     
17
     
18
   }
18
   }
19
 
19
 

+ 10
- 2
src/frontend/src/app/app.module.ts View File

5
  */
5
  */
6
 import { BrowserModule } from '@angular/platform-browser';
6
 import { BrowserModule } from '@angular/platform-browser';
7
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
7
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
8
-import { NgModule } from '@angular/core';
8
+import { NgModule, APP_INITIALIZER } from '@angular/core';
9
 import { HttpClientModule } from '@angular/common/http';
9
 import { HttpClientModule } from '@angular/common/http';
10
 import { CoreModule } from './@core/core.module';
10
 import { CoreModule } from './@core/core.module';
11
 import { ThemeModule } from './@theme/theme.module';
11
 import { ThemeModule } from './@theme/theme.module';
20
   NbToastrModule,
20
   NbToastrModule,
21
   NbWindowModule,
21
   NbWindowModule,
22
 } from '@nebular/theme';
22
 } from '@nebular/theme';
23
+import { ApiService, initializeLoginSvc } from './frontcraft/services/login-api';
24
+import { CookieService } from 'ngx-cookie-service';
23
 
25
 
24
 @NgModule({
26
 @NgModule({
25
   declarations: [AppComponent],
27
   declarations: [AppComponent],
42
   ],
44
   ],
43
   bootstrap: [AppComponent],
45
   bootstrap: [AppComponent],
44
   providers: [
46
   providers: [
45
-    
47
+    ApiService,
48
+    {
49
+        provide: APP_INITIALIZER,
50
+        deps: [CookieService],
51
+        multi: true,
52
+        useFactory: initializeLoginSvc
53
+    },
46
   ]
54
   ]
47
 })
55
 })
48
 export class AppModule {
56
 export class AppModule {

+ 2
- 2
src/frontend/src/app/frontcraft/auth/auth-layout.component.ts View File

1
 import { Component, OnInit } from '@angular/core';
1
 import { Component, OnInit } from '@angular/core';
2
-import { LoginApiService } from '../services/login-api';
2
+import { ApiService } from '../services/login-api';
3
 import { Router } from '@angular/router';
3
 import { Router } from '@angular/router';
4
 
4
 
5
 @Component({
5
 @Component({
13
 
13
 
14
 export class AuthComponent implements OnInit{
14
 export class AuthComponent implements OnInit{
15
   constructor(
15
   constructor(
16
-    private loginSvc : LoginApiService,
16
+    private loginSvc : ApiService,
17
     private router: Router  
17
     private router: Router  
18
   ){}
18
   ){}
19
 
19
 

+ 1
- 1
src/frontend/src/app/frontcraft/auth/login/login.component.html View File

3
 <form (ngSubmit)="login()" #form="ngForm" aria-labelledby="title">
3
 <form (ngSubmit)="login()" #form="ngForm" aria-labelledby="title">
4
 
4
 
5
   <div class="form-control-group">
5
   <div class="form-control-group">
6
-    <label class="label" for="input-email">Email address:</label>
6
+    <label class="label" for="input-email">Username:</label>
7
     <input nbInput
7
     <input nbInput
8
            fullWidth
8
            fullWidth
9
            [(ngModel)]="user.name"
9
            [(ngModel)]="user.name"

+ 2
- 2
src/frontend/src/app/frontcraft/auth/login/login.component.ts View File

1
 import { Component, OnInit } from '@angular/core';
1
 import { Component, OnInit } from '@angular/core';
2
-import { LoginApiService } from '../../services/login-api';
2
+import { ApiService } from '../../services/login-api';
3
 import { Router } from '@angular/router';
3
 import { Router } from '@angular/router';
4
 
4
 
5
 @Component({
5
 @Component({
21
 
21
 
22
   constructor(
22
   constructor(
23
     private router : Router,  
23
     private router : Router,  
24
-    private loginApi : LoginApiService
24
+    private loginApi : ApiService
25
   ){}
25
   ){}
26
 
26
 
27
   ngOnInit(){
27
   ngOnInit(){

+ 3
- 3
src/frontend/src/app/frontcraft/auth/logout/logout.component.ts View File

1
 import { Component, OnInit } from '@angular/core';
1
 import { Component, OnInit } from '@angular/core';
2
-import { LoginApiService } from '../../services/login-api';
2
+import { ApiService } from '../../services/login-api';
3
 import { Router } from '@angular/router';
3
 import { Router } from '@angular/router';
4
 
4
 
5
 @Component({
5
 @Component({
9
 export class LogoutComponent implements OnInit{
9
 export class LogoutComponent implements OnInit{
10
 
10
 
11
   constructor(
11
   constructor(
12
-    private loginApi : LoginApiService
12
+    private api : ApiService
13
   ){}
13
   ){}
14
 
14
 
15
   ngOnInit(){
15
   ngOnInit(){
16
-    this.loginApi.logout()
16
+    this.api.logout()
17
   }
17
   }
18
 
18
 
19
 }
19
 }

+ 0
- 15
src/frontend/src/app/frontcraft/auth/register/register.component.html View File

2
 
2
 
3
 <form (ngSubmit)="onSubmit()" #form="ngForm" aria-labelledby="title">
3
 <form (ngSubmit)="onSubmit()" #form="ngForm" aria-labelledby="title">
4
 
4
 
5
-  <div class="form-control-group">
6
-    <label class="label" for="input-email">Email address:</label>
7
-    <input nbInput
8
-           fullWidth
9
-           [(ngModel)]="user.email"
10
-           #email="ngModel"
11
-           name="name"
12
-           id="input-email"
13
-           pattern=".+"
14
-           placeholder="Email Address"
15
-           fieldSize="giant"
16
-           autofocus
17
-           [required]="true">
18
-  </div>
19
-
20
   <div class="form-control-group">
5
   <div class="form-control-group">
21
     <label class="label" for="input-username">Username:</label>
6
     <label class="label" for="input-username">Username:</label>
22
     <input nbInput
7
     <input nbInput

+ 15
- 15
src/frontend/src/app/frontcraft/auth/register/register.component.ts View File

1
 import { Component, OnInit } from '@angular/core';
1
 import { Component, OnInit } from '@angular/core';
2
-import { LoginApiService, hash } from '../../services/login-api';
2
+import { ApiService as ApiService, hash } from '../../services/login-api';
3
 import { Router } from '@angular/router';
3
 import { Router } from '@angular/router';
4
 import { _Rank, _Class, Class, User  } from '../../../../../../backend/Types/Types'
4
 import { _Rank, _Class, Class, User  } from '../../../../../../backend/Types/Types'
5
 import { specs  } from '../../../../../../backend/Types/PlayerSpecs'
5
 import { specs  } from '../../../../../../backend/Types/PlayerSpecs'
6
+import { race } from 'rxjs';
6
 
7
 
7
 
8
 
8
 @Component({
9
 @Component({
20
     spec : "Arms"
21
     spec : "Arms"
21
   }
22
   }
22
 
23
 
23
-
24
   ranks = _Rank
24
   ranks = _Rank
25
   classes = _Class
25
   classes = _Class
26
   selectedClass : Class = "Warrior"
26
   selectedClass : Class = "Warrior"
30
 
30
 
31
   constructor(
31
   constructor(
32
     private router : Router,  
32
     private router : Router,  
33
-    private loginApi : LoginApiService
33
+    private api : ApiService
34
   ){}
34
   ){}
35
 
35
 
36
   ngOnInit(){
36
   ngOnInit(){
37
-    this.loginApi.checkLogin().then(loggedin => {
37
+    this.api.checkLogin().then(loggedin => {
38
       if(loggedin){
38
       if(loggedin){
39
         this.router.navigateByUrl("/")
39
         this.router.navigateByUrl("/")
40
       }
40
       }
60
     this.character = {}
60
     this.character = {}
61
 
61
 
62
     try{
62
     try{
63
-      const usr = await this.loginApi.getUnprivilegedSocket().Authenticator.createUser(user)
63
+      const usr = await this.api.get('UserManager').createUser(user)
64
     }catch(e){
64
     }catch(e){
65
       alert("Error creating user"+e)
65
       alert("Error creating user"+e)
66
       return
66
       return
67
     }
67
     }
68
 
68
 
69
     try{
69
     try{
70
-      await this.loginApi.login(user.username, pw)
71
-      await this.loginApi.getFeature('createCharacter').then(async feature => {
72
-        if(!feature) return
73
-        const specid = await this.loginApi.getUnprivilegedSocket().CharacterManager.getSpecId(char['class'], char['spec'])
74
-        await feature.createCharacter(this.loginApi.getAuth().token.value, {
75
-          charactername: char.name,
76
-          specid: specid,
77
-          userid: this.loginApi.getAuth().user.id!
78
-        })
70
+      await this.api.login(user.username, pw)
71
+
72
+      const characterManager = this.api.get('CharacterManager')
73
+
74
+      const specid = await characterManager.getSpecId(char['class'], char['spec'])
75
+      await characterManager.createCharacter(this.api.getAuth().token.value, {
76
+        charactername: char.name,
77
+        specid: specid,
78
+        userid: this.api.getAuth().user.id!,
79
+        race: 'Human'
79
       })
80
       })
80
     }catch(e){
81
     }catch(e){
81
       alert("Error creating character"+e)
82
       alert("Error creating character"+e)
82
       return
83
       return
83
     }
84
     }
84
   }
85
   }
85
-
86
 }
86
 }

+ 13
- 0
src/frontend/src/app/frontcraft/pages/character/character.component.html View File

1
 <nb-card 
1
 <nb-card 
2
+class = "col-12 col-xl-9"
2
 status="control">
3
 status="control">
3
     <nb-card-header [ngStyle]="{'color': color}" style="text-transform: capitalize;">
4
     <nb-card-header [ngStyle]="{'color': color}" style="text-transform: capitalize;">
4
         {{char.charactername}}
5
         {{char.charactername}}
5
     </nb-card-header>
6
     </nb-card-header>
6
     <nb-card-body>
7
     <nb-card-body>
8
+        {{char.race}}<br />
7
         {{char.specname}} {{char.class}}<br />
9
         {{char.specname}} {{char.class}}<br />
8
         Owned by <a [routerLink]="'/frontcraft/user/'+char.username"> {{char.username}} ({{char.rank}})</a>
10
         Owned by <a [routerLink]="'/frontcraft/user/'+char.username"> {{char.username}} ({{char.rank}})</a>
11
+        <br/><br />
12
+        <span *ngFor="let token of tokens">
13
+            [ {{token.level}} ]
14
+            <a [ngStyle]="{'color':token.quality=='Epic'?'#a335ee':'#ff8000'}" 
15
+            target="_blank" 
16
+            [href]="token.url">
17
+            <img style="min-width: 25px; width: 2.25vw; max-width: 50px" 
18
+            [src]="'https://wow.zamimg.com/images/wow/icons/large/'+token.iconname+'.jpg'" />
19
+            {{token.itemname}}
20
+            </a><br />
21
+        </span>
9
     </nb-card-body>
22
     </nb-card-body>
10
 </nb-card>
23
 </nb-card>

+ 7
- 5
src/frontend/src/app/frontcraft/pages/character/character.component.ts View File

1
 import { Component, OnInit } from '@angular/core';
1
 import { Component, OnInit } from '@angular/core';
2
 import { ActivatedRoute, Router } from '@angular/router';
2
 import { ActivatedRoute, Router } from '@angular/router';
3
-import { LoginApiService } from '../../services/login-api';
3
+import { ApiService as ApiService } from '../../services/login-api';
4
 import { Spec, User, Character } from '../../../../../../backend/Types/Types';
4
 import { Spec, User, Character } from '../../../../../../backend/Types/Types';
5
 import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
5
 import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
6
 
6
 
12
 
12
 
13
     char : (Character & User & Spec) = {} as any
13
     char : (Character & User & Spec) = {} as any
14
     color : string
14
     color : string
15
+    tokens
15
 
16
 
16
     constructor(
17
     constructor(
17
-      private login: LoginApiService,
18
+      private api: ApiService,
18
       private route: ActivatedRoute,
19
       private route: ActivatedRoute,
19
-      private router: Router,
20
     ){}
20
     ){}
21
 
21
 
22
     async ngOnInit(){
22
     async ngOnInit(){
23
       const param = this.route.snapshot.paramMap.get('name');
23
       const param = this.route.snapshot.paramMap.get('name');
24
-      this.login.getUnprivilegedSocket()      
25
-      .CharacterManager
24
+      this.api.get('CharacterManager')      
26
       .getCharacterByName(param)
25
       .getCharacterByName(param)
27
       .then((char) => {
26
       .then((char) => {
28
         if(char){
27
         if(char){
29
           this.color = getClassColor(char.class)
28
           this.color = getClassColor(char.class)
30
           this.char = char
29
           this.char = char
30
+          this.api.get('ItemManager').getTokens(this.char).then(tokens => {
31
+            this.tokens = tokens
32
+          })
31
         }
33
         }
32
       })
34
       })
33
     }
35
     }

+ 34
- 0
src/frontend/src/app/frontcraft/pages/characters/characters.component.html View File

1
+<nb-card class="col-12 col-xl-9">
2
+    <nb-card-body>
3
+
4
+        <div echarts [options]="options" class="echart" (chartClick)="onChartClick($event)"></div>
5
+        
6
+    </nb-card-body>
7
+</nb-card>
8
+
9
+
10
+
11
+
12
+<nb-card class="col-12 col-xl-9">
13
+    <nb-card-body>
14
+        <input type="text" nbInput [(ngModel)]="search" (change)="changeSearch()" placeholder="Search" fullWidth="true" />
15
+        <table class="col-12">
16
+                <tr *ngFor="let character of displayedcharacters">
17
+                    <td>
18
+                        <a style="text-transform: capitalize;"  [routerLink]="'/frontcraft/character/'+character.charactername" [ngStyle]="{'color': character.color}">
19
+                            {{character.charactername}} <span *ngIf="character.alt">(Alt)</span>
20
+                        </a>
21
+                    </td>
22
+                    <td>
23
+                        {{character.race}}
24
+                    </td>
25
+                    <td>
26
+                        {{character.specname}}
27
+                    </td>
28
+                    <td>
29
+                        {{character.class}}
30
+                    </td>
31
+                </tr>
32
+        </table>
33
+    </nb-card-body>
34
+</nb-card>

+ 123
- 0
src/frontend/src/app/frontcraft/pages/characters/characters.component.ts View File

1
+import { Component, OnInit, AfterViewInit, OnDestroy } from '@angular/core';
2
+import { NbThemeService } from '@nebular/theme';
3
+import { ApiService } from '../../services/login-api';
4
+import { _Class } from '../../../../../../backend/Types/Types';
5
+import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
6
+
7
+
8
+@Component({
9
+  selector: 'characters',
10
+  templateUrl: './characters.component.html',
11
+})
12
+export class FrontcraftCharactersComponent implements AfterViewInit, OnDestroy{
13
+  options: any = {};
14
+  themeSubscription: any;
15
+  search = ""
16
+
17
+  allcharacters = []
18
+  displayedcharacters = []
19
+
20
+  constructor(private theme: NbThemeService, private api:ApiService) {
21
+  }
22
+
23
+  changeSearch(){
24
+    if(!this.search || this.search == "") 
25
+      this.displayedcharacters = this.allcharacters
26
+    else{
27
+      const searchterm = this.search.toLocaleLowerCase()
28
+      this.displayedcharacters = this.allcharacters.filter(char => 
29
+        char.charactername.toLowerCase().includes(searchterm) ||
30
+        char.class.toLowerCase().includes(searchterm) ||
31
+        char.specname.toLowerCase().includes(searchterm) ||
32
+        char.race.toLowerCase().includes(searchterm)
33
+      )
34
+    }
35
+  }
36
+
37
+  onChartClick(event){
38
+    this.search = event.name
39
+    this.changeSearch()
40
+  }
41
+
42
+  ngAfterViewInit() {
43
+    this.themeSubscription = this.theme.getJsTheme().subscribe(async config => {
44
+      const data = {}
45
+
46
+      await this.api.get('CharacterManager').getCharacters().then(chars => {
47
+        chars.forEach(char => {
48
+          char['color'] = getClassColor(char.class)
49
+        })
50
+        this.allcharacters = chars
51
+        this.displayedcharacters = chars
52
+        this.search=""
53
+      })
54
+
55
+      await Promise.all(_Class.map(async cls => {
56
+        const c = await this.api.get('CharacterManager').getHeadCount(cls)
57
+        data[cls] = c
58
+      }))
59
+      
60
+      const echarts: any = config.variables.echarts;
61
+      this.options = {
62
+        backgroundColor: echarts.bg,
63
+        color: [
64
+          "#FFF569",
65
+          "#C79C6E",
66
+          "#FFFFFF",
67
+          "#40C7EB",
68
+          "#A9D271",
69
+          "#F58CBA",
70
+          "#FF7D0A",
71
+          "#8787ED"
72
+        ],
73
+        tooltip: {
74
+          trigger: 'item',
75
+          formatter: '{b} : {c} ({d}%)',
76
+        },
77
+        series: [
78
+          {
79
+            name: 'Classes',
80
+            type: 'pie',
81
+            radius: '80%',
82
+            center: ['50%', '50%'],
83
+            data: [
84
+              { value: data['Rogue'], name: 'Rogue' },
85
+              { value: data['Warrior'], name: 'Warrior' },
86
+              { value: data['Priest'], name: 'Priest' },
87
+              { value: data['Mage'], name: 'Mage' },
88
+              { value: data['Hunter'], name: 'Hunter' },
89
+              { value: data['Paladin'], name: 'Paladin' },
90
+              { value: data['Druid'], name: 'Druid' },
91
+              { value: data['Warlock'], name: 'Warlock' },
92
+            ],
93
+            itemStyle: {
94
+              emphasis: {
95
+                shadowBlur: 10,
96
+                shadowOffsetX: 0,
97
+                shadowColor: echarts.itemHoverShadowColor,
98
+              },
99
+            },
100
+            label: {
101
+              normal: {
102
+                textStyle: {
103
+                  color: echarts.textColor,
104
+                },
105
+              },
106
+            },
107
+            labelLine: {
108
+              normal: {
109
+                lineStyle: {
110
+                  color: echarts.axisLineColor,
111
+                },
112
+              },
113
+            },
114
+          },
115
+        ],
116
+      };
117
+    });
118
+  }
119
+
120
+  ngOnDestroy(): void {
121
+    this.themeSubscription.unsubscribe();
122
+  }
123
+}

+ 0
- 35
src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.html View File

1
-<nb-card>
2
-    <nb-card-body>
3
-
4
-        <label class="search-label" for="search">Search:</label>
5
-        <input nbInput [nbFilterInput]="dataSource" id="search" class="search-input">
6
-
7
-        <table [nbTreeGrid]="dataSource" [nbSort]="dataSource" (sort)="updateSort($event)">
8
-
9
-            <tr nbTreeGridHeaderRow *nbTreeGridHeaderRowDef="allColumns"></tr>
10
-            <tr nbTreeGridRow *nbTreeGridRowDef="let row; columns: allColumns"></tr>
11
-
12
-            <ng-container [nbTreeGridColumnDef]="customColumn">
13
-                <th nbTreeGridHeaderCell [nbSortHeader]="getSortDirection(customColumn)" *nbTreeGridHeaderCellDef>
14
-                {{customColumn}}
15
-                </th>
16
-                <td nbTreeGridCell *nbTreeGridCellDef="let row">
17
-                    <nb-tree-grid-row-toggle
18
-                        *ngIf="row.children && row.children.length">
19
-                    </nb-tree-grid-row-toggle>
20
-                    
21
-                    {{row.data[customColumn]}}
22
-                </td>
23
-            </ng-container>
24
-
25
-            <ng-container *ngFor="let column of defaultColumns; let index = index"
26
-                            [nbTreeGridColumnDef]="column"
27
-                            [showOn]="getShowOn(index)">
28
-                <th nbTreeGridHeaderCell [nbSortHeader]="getSortDirection(column)" *nbTreeGridHeaderCellDef>
29
-                {{column}}
30
-                </th>
31
-                <td nbTreeGridCell *nbTreeGridCellDef="let row">{{row.data[column] || '-'}}</td>
32
-            </ng-container>
33
-        </table>
34
-    </nb-card-body>
35
-</nb-card>

+ 0
- 42
src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.scss View File

1
-button[nbTreeGridRowToggle] {
2
-    background: transparent;
3
-    border: none;
4
-    padding: 0;
5
-  }
6
-  
7
-  .search-label {
8
-    display: block;
9
-  }
10
-  .search-input {
11
-    margin-bottom: 1rem;
12
-  }
13
-  
14
-  .nb-column-name {
15
-    width: 100%;
16
-  }
17
-  
18
-  @media screen and (min-width: 400px) {
19
-    .nb-column-name,
20
-    .nb-column-size {
21
-      width: 50%;
22
-    }
23
-  }
24
-  
25
-  @media screen and (min-width: 500px) {
26
-    .nb-column-name,
27
-    .nb-column-size,
28
-    .nb-column-kind {
29
-      width: 33.333%;
30
-    }
31
-  }
32
-  
33
-  @media screen and (min-width: 600px) {
34
-    .nb-column-name {
35
-      width: 31%;
36
-    }
37
-    .nb-column-size,
38
-    .nb-column-kind,
39
-    .nb-column-items {
40
-      width: 23%;
41
-    }
42
-  }

+ 0
- 63
src/frontend/src/app/frontcraft/pages/dashboard/dashboard.component.ts View File

1
-import { Component } from '@angular/core';
2
-import { NbSortDirection, NbSortRequest, NbTreeGridDataSourceBuilder, NbTreeGridDataSource } from '@nebular/theme';
3
-
4
-interface TreeNode<T> {
5
-  data: T;
6
-  children?: TreeNode<T>[];
7
-  expanded?: boolean;
8
-}
9
-
10
-interface Row {
11
-  name: string,
12
-  character: any,
13
-  SRC: any,
14
-}
15
-
16
-type TreeType = TreeNode<Row>
17
-
18
-@Component({
19
-  selector: 'dashboard',
20
-  templateUrl: './dashboard.component.html',
21
-  styleUrls: ['./tree-grid-shared.scss', './dashboard.component.scss'],
22
-})
23
-export class FrontcraftDashboardComponent{
24
-  customColumn = 'name';
25
-  defaultColumns = [ 'character', 'SRC'/*, 'Profile'*/ ];
26
-  allColumns = [ this.customColumn, ...this.defaultColumns ];
27
-
28
-  dataSource: NbTreeGridDataSource<TreeType>;
29
-
30
-  sortColumn: string;
31
-  sortDirection: NbSortDirection = NbSortDirection.NONE;
32
-
33
-  constructor(private dataSourceBuilder: NbTreeGridDataSourceBuilder<TreeType>) {
34
-    this.dataSource = this.dataSourceBuilder.create(this.data);
35
-  }
36
-
37
-  updateSort(sortRequest: NbSortRequest): void {
38
-    this.sortColumn = sortRequest.column;
39
-    this.sortDirection = sortRequest.direction;
40
-  }
41
-
42
-  getSortDirection(column: string): NbSortDirection {
43
-    if (this.sortColumn === column) {
44
-      return this.sortDirection;
45
-    }
46
-    return NbSortDirection.NONE;
47
-  }
48
-
49
-  private data: TreeType[] = [
50
-   {
51
-     data: {name: 'a', character: 2, SRC: 0},
52
-     children: [
53
-       {data: {name: 'a', character: 'Warrior', SRC: 'Arms'}}
54
-     ]
55
-   }
56
-  ];
57
-
58
-  getShowOn(index: number) {
59
-    const minWithForMultipleColumns = 400;
60
-    const nextColumnStep = 100;
61
-    return minWithForMultipleColumns + (nextColumnStep * index);
62
-  }
63
-}

+ 0
- 10
src/frontend/src/app/frontcraft/pages/dashboard/tree-grid-shared.scss View File

1
-::ng-deep {
2
-    body {
3
-      min-height: 20rem;
4
-    }
5
-  
6
-    .nb-tree-grid-header-cell,
7
-    .nb-tree-grid-header-cell button {
8
-      text-transform: capitalize;
9
-    }
10
-  }

+ 18
- 18
src/frontend/src/app/frontcraft/pages/pages-layout.component.ts View File

1
-import { Component, AfterContentChecked, OnInit } from '@angular/core';
1
+import { Component, AfterContentChecked, OnInit, AfterContentInit } from '@angular/core';
2
 import { NbMenuItem } from '@nebular/theme';
2
 import { NbMenuItem } from '@nebular/theme';
3
-import { LoginApiService } from '../services/login-api';
3
+import { ApiService } from '../services/login-api';
4
 import { Router } from '@angular/router';
4
 import { Router } from '@angular/router';
5
 
5
 
6
 @Component({
6
 @Component({
9
 
9
 
10
     <ngx-one-column-layout>
10
     <ngx-one-column-layout>
11
     <nb-menu [items]="menu"></nb-menu>
11
     <nb-menu [items]="menu"></nb-menu>
12
-
13
-      <router-outlet></router-outlet>
12
+        <router-outlet ></router-outlet>
14
     </ngx-one-column-layout>
13
     </ngx-one-column-layout>
15
   `,
14
   `,
16
 })
15
 })
17
-export class PagesLayoutComponent implements OnInit{
16
+export class PagesLayoutComponent implements AfterContentInit{
18
   menu:NbMenuItem[] = [{
17
   menu:NbMenuItem[] = [{
19
-    icon: 'people-outline',
20
-    title: 'People',
21
-    link: '/frontcraft/people'
18
+    icon: 'image',
19
+    title: 'Raids',
20
+    link: '/frontcraft/raids',
22
   },{
21
   },{
23
-    icon: 'clock-outline',
24
-    title: 'Raids'
22
+    icon: 'people',
23
+    title: 'Characters',
24
+    link: '/frontcraft/characters',
25
   },{
25
   },{
26
-    icon: 'clock-outline',
27
-    title: 'character',
28
-    link: '/frontcraft/character/a'
26
+    icon: 'shopping-cart',
27
+    title: 'Token Shop',
28
+    link: '/frontcraft/shop',
29
   },{
29
   },{
30
-    icon: 'clock-outline',
31
-    title: 'user',
32
-    link: '/frontcraft/user/a'
30
+    icon: 'book-open-outline',
31
+    title: 'Loot Rules',
32
+    link: '/frontcraft/rules',
33
   }]
33
   }]
34
   
34
   
35
   constructor(
35
   constructor(
36
-    private loginSvc : LoginApiService,
36
+    private loginSvc : ApiService,
37
     private router : Router
37
     private router : Router
38
   ){}
38
   ){}
39
 
39
 
40
-  ngOnInit() : void {
40
+  ngAfterContentInit() : void {
41
     this.loginSvc.checkLogin().then(loggedin => {
41
     this.loginSvc.checkLogin().then(loggedin => {
42
       if(!loggedin){
42
       if(!loggedin){
43
         this.router.navigateByUrl("/auth")
43
         this.router.navigateByUrl("/auth")

+ 29
- 9
src/frontend/src/app/frontcraft/pages/pages-routing.module.ts View File

1
 import { NgModule } from '@angular/core';
1
 import { NgModule } from '@angular/core';
2
 import { RouterModule, Routes } from '@angular/router';
2
 import { RouterModule, Routes } from '@angular/router';
3
-import { FrontcraftDashboardComponent } from './dashboard/dashboard.component';
4
 import { PagesLayoutComponent } from './pages-layout.component';
3
 import { PagesLayoutComponent } from './pages-layout.component';
5
 import { FrontcraftCharacterComponent } from './character/character.component';
4
 import { FrontcraftCharacterComponent } from './character/character.component';
6
 import { FrontcraftUserComponent } from './user/user.component';
5
 import { FrontcraftUserComponent } from './user/user.component';
7
-import { FrontcraftPeopleComponent } from './people/people.component';
6
+import { FrontcraftRaidsComponent } from './raids/raids.component';
7
+import { FrontcraftRaidComponent } from './raid/raid.component';
8
+import { FrontcraftArchiveComponent } from './raid/archive.component';
9
+import { FrontcraftShopComponent } from './shop/shop.component';
10
+import { FrontcraftRulesComponent } from './rules/rules.component';
11
+import { FrontcraftCharactersComponent } from './characters/characters.component';
8
 
12
 
9
 export const routes: Routes = [
13
 export const routes: Routes = [
10
     {
14
     {
11
         path: '',
15
         path: '',
12
         component: PagesLayoutComponent,
16
         component: PagesLayoutComponent,
13
         children: [
17
         children: [
14
-            {
15
-                path: 'people',
16
-                component: FrontcraftPeopleComponent
17
-            },
18
             {
18
             {
19
                 path: 'user/:name',
19
                 path: 'user/:name',
20
                 component: FrontcraftUserComponent
20
                 component: FrontcraftUserComponent
21
             },
21
             },
22
+            {
23
+                path: 'raid/:id',
24
+                component: FrontcraftRaidComponent
25
+            },
26
+            {
27
+                path: 'archive/:id',
28
+                component: FrontcraftArchiveComponent
29
+            },
30
+            {
31
+                path: 'raids',
32
+                component: FrontcraftRaidsComponent
33
+            },
22
             {
34
             {
23
                 path: 'character/:name',
35
                 path: 'character/:name',
24
                 component: FrontcraftCharacterComponent
36
                 component: FrontcraftCharacterComponent
25
             },
37
             },
26
             {
38
             {
27
-                path: 'dashboard',
28
-                component: FrontcraftDashboardComponent,
39
+                path: 'characters',
40
+                component: FrontcraftCharactersComponent,
41
+            },
42
+            {
43
+                path: 'shop',
44
+                component: FrontcraftShopComponent
45
+            },
46
+            {
47
+                path: 'rules',
48
+                component: FrontcraftRulesComponent
29
             },
49
             },
30
             {
50
             {
31
                 path: '**',
51
                 path: '**',
32
-                redirectTo: 'dashboard'
52
+                redirectTo: 'raids'
33
             },
53
             },
34
         ]
54
         ]
35
     }    
55
     }    

+ 44
- 7
src/frontend/src/app/frontcraft/pages/pages.module.ts View File

2
 import { NgModule } from '@angular/core';
2
 import { NgModule } from '@angular/core';
3
 import { FormsModule } from '@angular/forms';
3
 import { FormsModule } from '@angular/forms';
4
 import { RouterModule } from '@angular/router';
4
 import { RouterModule } from '@angular/router';
5
-
6
 import { 
5
 import { 
7
   NbAlertModule,
6
   NbAlertModule,
8
   NbButtonModule,
7
   NbButtonModule,
10
   NbInputModule,
9
   NbInputModule,
11
   NbMenuModule,
10
   NbMenuModule,
12
   NbCardModule,
11
   NbCardModule,
13
-  NbTreeGridModule
12
+  NbTreeGridModule,
13
+  NbListModule,
14
+  NbTabsetModule,
15
+  NbIconModule,
16
+  NbWindowModule,
17
+  NbPopoverModule,
18
+  NbDatepickerModule,
19
+  NbSelectModule
14
 } from '@nebular/theme';
20
 } from '@nebular/theme';
15
 import { MyAuthRoutingModule } from './pages-routing.module';
21
 import { MyAuthRoutingModule } from './pages-routing.module';
16
-import { FrontcraftDashboardComponent } from './dashboard/dashboard.component';
17
 import { PagesLayoutComponent } from './pages-layout.component';
22
 import { PagesLayoutComponent } from './pages-layout.component';
18
 import { ThemeModule } from '../../@theme/theme.module';
23
 import { ThemeModule } from '../../@theme/theme.module';
19
 import { DashboardModule } from '../../demo_pages/dashboard/dashboard.module';
24
 import { DashboardModule } from '../../demo_pages/dashboard/dashboard.module';
21
 import { MiscellaneousModule } from '../../demo_pages/miscellaneous/miscellaneous.module';
26
 import { MiscellaneousModule } from '../../demo_pages/miscellaneous/miscellaneous.module';
22
 import { FrontcraftCharacterComponent } from './character/character.component';
27
 import { FrontcraftCharacterComponent } from './character/character.component';
23
 import { FrontcraftUserComponent } from './user/user.component';
28
 import { FrontcraftUserComponent } from './user/user.component';
24
-import { FrontcraftPeopleComponent } from './people/people.component';
29
+import { FrontcraftRaidsComponent } from './raids/raids.component';
30
+import { FrontcraftCreateRaidsComponent } from './raids/createraid.compontent';
31
+import { FrontcraftRaidComponent } from './raid/raid.component';
32
+import { FrontcraftArchiveComponent } from './raid/archive.component';
33
+import { NbEvaIconsModule } from '@nebular/eva-icons';
34
+import { FrontcraftCharacerpickerComponent } from './raid/characterpicker.component';
35
+import { FrontcraftShopComponent, FrontcraftBuyTokenComponent } from './shop/shop.component';
36
+import { FrontcraftRulesComponent } from './rules/rules.component';
37
+import { FrontcraftItemSelectComponent } from './shop/itemselector.component';
38
+import { NgxEchartsModule } from 'ngx-echarts';
39
+import { FrontcraftCharactersComponent } from './characters/characters.component';
25
 
40
 
26
 
41
 
27
 @NgModule({
42
 @NgModule({
35
     NbInputModule,
50
     NbInputModule,
36
     NbButtonModule,
51
     NbButtonModule,
37
     NbCheckboxModule,
52
     NbCheckboxModule,
53
+    NbTabsetModule,
38
     ThemeModule,
54
     ThemeModule,
39
     NbMenuModule,
55
     NbMenuModule,
40
     DashboardModule,
56
     DashboardModule,
41
     ECommerceModule,
57
     ECommerceModule,
42
     MiscellaneousModule,
58
     MiscellaneousModule,
43
     NbCardModule,
59
     NbCardModule,
44
-    
60
+    NbListModule,
61
+    NbIconModule,
62
+    NbEvaIconsModule,
63
+    NbPopoverModule,
64
+    NbDatepickerModule,
65
+    NbSelectModule,
66
+    NgxEchartsModule,
67
+    NbWindowModule.forChild(),
45
   ],
68
   ],
46
   declarations: [
69
   declarations: [
47
-    FrontcraftPeopleComponent,
70
+    FrontcraftItemSelectComponent,
71
+    FrontcraftRaidComponent,
72
+    FrontcraftRaidsComponent,
48
     FrontcraftUserComponent,
73
     FrontcraftUserComponent,
49
     FrontcraftCharacterComponent,
74
     FrontcraftCharacterComponent,
50
     PagesLayoutComponent,
75
     PagesLayoutComponent,
51
-    FrontcraftDashboardComponent
76
+    FrontcraftCharactersComponent,
77
+    FrontcraftCharacerpickerComponent,
78
+    FrontcraftArchiveComponent,
79
+    FrontcraftShopComponent,
80
+    FrontcraftRulesComponent,
81
+    FrontcraftCreateRaidsComponent,
82
+    FrontcraftBuyTokenComponent,
52
   ],
83
   ],
84
+  entryComponents: [
85
+    FrontcraftItemSelectComponent,
86
+    FrontcraftCharacerpickerComponent,
87
+    FrontcraftCreateRaidsComponent,
88
+    FrontcraftBuyTokenComponent,
89
+  ]
53
 })
90
 })
54
 export class FrontcraftPagesModule {
91
 export class FrontcraftPagesModule {
55
 }
92
 }

+ 0
- 36
src/frontend/src/app/frontcraft/pages/people/people.component.html View File

1
-<nb-card>
2
-    <nb-card-body>
3
-
4
-        <label class="search-label" for="search">Search:</label>
5
-        <input nbInput [nbFilterInput]="dataSource" id="search" class="search-input">
6
-
7
-        <table [nbTreeGrid]="dataSource" [nbSort]="dataSource" (sort)="updateSort($event)">
8
-
9
-            <tr nbTreeGridHeaderRow *nbTreeGridHeaderRowDef="allColumns"></tr>
10
-            <tr nbTreeGridRow *nbTreeGridRowDef="let row; columns: allColumns"></tr>
11
-
12
-            <ng-container [nbTreeGridColumnDef]="customColumn">
13
-                <th nbTreeGridHeaderCell [nbSortHeader]="getSortDirection(customColumn)" *nbTreeGridHeaderCellDef>
14
-                {{customColumn}}
15
-                </th>
16
-                <td nbTreeGridCell *nbTreeGridCellDef="let row">
17
-                    <nb-tree-grid-row-toggle
18
-                        *ngIf="row.children && row.children.length">
19
-                    </nb-tree-grid-row-toggle>
20
-                    <span style="text-transform: capitalize;">
21
-                        {{row.data[customColumn]}}
22
-                    </span>
23
-                </td>
24
-            </ng-container>
25
-
26
-            <ng-container *ngFor="let column of defaultColumns; let index = index"
27
-                            [nbTreeGridColumnDef]="column"
28
-                            [showOn]="getShowOn(index)">
29
-                <th nbTreeGridHeaderCell [nbSortHeader]="getSortDirection(column)" *nbTreeGridHeaderCellDef>
30
-                {{column}}
31
-                </th>
32
-                <td nbTreeGridCell *nbTreeGridCellDef="let row">{{row.data[column] || '-'}}</td>
33
-            </ng-container>
34
-        </table>
35
-    </nb-card-body>
36
-</nb-card>

+ 0
- 42
src/frontend/src/app/frontcraft/pages/people/people.component.scss View File

1
-button[nbTreeGridRowToggle] {
2
-    background: transparent;
3
-    border: none;
4
-    padding: 0;
5
-  }
6
-  
7
-  .search-label {
8
-    display: block;
9
-  }
10
-  .search-input {
11
-    margin-bottom: 1rem;
12
-  }
13
-  
14
-  .nb-column-name {
15
-    width: 100%;
16
-  }
17
-  
18
-  @media screen and (min-width: 400px) {
19
-    .nb-column-name,
20
-    .nb-column-size {
21
-      width: 50%;
22
-    }
23
-  }
24
-  
25
-  @media screen and (min-width: 500px) {
26
-    .nb-column-name,
27
-    .nb-column-size,
28
-    .nb-column-kind {
29
-      width: 33.333%;
30
-    }
31
-  }
32
-  
33
-  @media screen and (min-width: 600px) {
34
-    .nb-column-name {
35
-      width: 31%;
36
-    }
37
-    .nb-column-size,
38
-    .nb-column-kind,
39
-    .nb-column-items {
40
-      width: 23%;
41
-    }
42
-  }

+ 0
- 64
src/frontend/src/app/frontcraft/pages/people/people.component.ts View File

1
-import { Component } from '@angular/core';
2
-import { NbSortDirection, NbSortRequest, NbTreeGridDataSourceBuilder, NbTreeGridDataSource } from '@nebular/theme';
3
-
4
-interface TreeNode<T> {
5
-  data: T;
6
-  children?: TreeNode<T>[];
7
-  expanded?: boolean;
8
-}
9
-
10
-interface Row {
11
-  name: string,
12
-  character: any,
13
-  SRC: any,
14
-  kind: 'Character' | 'Account'
15
-}
16
-
17
-type TreeType = TreeNode<Row>
18
-
19
-@Component({
20
-  selector: 'people-component',
21
-  templateUrl: './people.component.html',
22
-  styleUrls: ['./tree-grid-shared.scss', './people.component.scss'],
23
-})
24
-export class FrontcraftPeopleComponent{
25
-  customColumn = 'name';
26
-  defaultColumns = [ 'character', 'SRC'/*, 'Profile'*/ ];
27
-  allColumns = [ this.customColumn, ...this.defaultColumns ];
28
-
29
-  dataSource: NbTreeGridDataSource<TreeType>;
30
-
31
-  sortColumn: string;
32
-  sortDirection: NbSortDirection = NbSortDirection.NONE;
33
-
34
-  constructor(private dataSourceBuilder: NbTreeGridDataSourceBuilder<TreeType>) {
35
-    this.dataSource = this.dataSourceBuilder.create(this.data);
36
-  }
37
-
38
-  updateSort(sortRequest: NbSortRequest): void {
39
-    this.sortColumn = sortRequest.column;
40
-    this.sortDirection = sortRequest.direction;
41
-  }
42
-
43
-  getSortDirection(column: string): NbSortDirection {
44
-    if (this.sortColumn === column) {
45
-      return this.sortDirection;
46
-    }
47
-    return NbSortDirection.NONE;
48
-  }
49
-
50
-  private data: TreeType[] = [
51
-   {
52
-     data: {name: 'a', character: 2, SRC: 0, kind: 'Account'},
53
-     children: [
54
-       {data: {name: 'a', character: 'Warrior', SRC: 'Arms', kind: 'Character'}}
55
-     ]
56
-   }
57
-  ];
58
-
59
-  getShowOn(index: number) {
60
-    const minWithForMultipleColumns = 400;
61
-    const nextColumnStep = 100;
62
-    return minWithForMultipleColumns + (nextColumnStep * index);
63
-  }
64
-}

+ 0
- 10
src/frontend/src/app/frontcraft/pages/people/tree-grid-shared.scss View File

1
-::ng-deep {
2
-    body {
3
-      min-height: 20rem;
4
-    }
5
-  
6
-    .nb-tree-grid-header-cell,
7
-    .nb-tree-grid-header-cell button {
8
-      text-transform: capitalize;
9
-    }
10
-  }

+ 64
- 0
src/frontend/src/app/frontcraft/pages/raid/archive.component.ts View File

1
+import { Component, OnInit } from '@angular/core';
2
+import { ActivatedRoute } from '@angular/router';
3
+import { ApiService as ApiService } from '../../services/login-api';
4
+import { RaidData } from '../../../../../../backend/Types/Types';
5
+import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
6
+
7
+@Component({
8
+  selector: 'archive',
9
+  templateUrl: './raid.component.html',
10
+})
11
+export class FrontcraftArchiveComponent implements OnInit{
12
+
13
+    canSignup = false
14
+    isSignedup = false
15
+    canManage = false
16
+    mySignup
17
+
18
+    raid: RaidData = <any>{
19
+      participants:{
20
+          Druid: [],
21
+          Hunter: [],
22
+          Mage: [],
23
+          Paladin: [],
24
+          Priest: [],
25
+          Rogue: [],
26
+          Shaman: [],
27
+          Warlock: [],
28
+          Warrior: [],
29
+      },
30
+      tokens:{}
31
+    }
32
+    
33
+    constructor(
34
+      private api: ApiService,
35
+      private route: ActivatedRoute,
36
+    ){
37
+    }
38
+
39
+    async ngOnInit(){
40
+      this.refresh()
41
+    }
42
+
43
+    refresh = async () => {
44
+      const param = this.route.snapshot.paramMap.get('id');
45
+
46
+      const raidManager = this.api.get('RaidManager')
47
+      const raiddata = await raidManager.getArchiveRaid(parseInt(param))
48
+      this.raid = raiddata
49
+
50
+      Object.values(raiddata.participants).flat().forEach(p => {
51
+        p['color'] = getClassColor(p.class)
52
+      })
53
+      
54
+      const matchingSignup = Object.values(raiddata.participants).flat().find(char => char.userid === this.api.getCurrentUser()!.id!)
55
+      if(matchingSignup){
56
+        this.isSignedup = true
57
+        this.mySignup = matchingSignup
58
+        this.mySignup.status = matchingSignup['benched']?'Bench':matchingSignup['late']?'Late':'Attending'
59
+      }else{
60
+        this.isSignedup = false
61
+        this.mySignup = null
62
+      }
63
+    } 
64
+}

+ 30
- 0
src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.html View File

1
+<nb-card class="col-12 col-xl-9">
2
+    <nb-card-header>
3
+      Pick Character
4
+    </nb-card-header>
5
+    <nb-card-body>
6
+        <div *ngFor="let character of characters">
7
+            <button
8
+                (click)="signup(character, false)"
9
+                nbButton 
10
+                outline 
11
+                status="success" 
12
+                size="tiny">
13
+                <nb-icon icon="checkmark-outline"></nb-icon>
14
+            </button>
15
+
16
+            <button
17
+                (click)="signup(character, true)"
18
+                nbButton 
19
+                outline 
20
+                status="warning" 
21
+                size="tiny">
22
+                <nb-icon icon="clock-outline"></nb-icon>
23
+            </button>
24
+            <span [ngStyle]="{'color': character.color}">
25
+                {{character.charactername}}
26
+            </span>
27
+                
28
+        </div>
29
+    </nb-card-body>
30
+</nb-card>

+ 44
- 0
src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.ts View File

1
+import { OnInit, Component } from '@angular/core';
2
+import { NbWindowRef, NbToastrService, NbDialogRef } from '@nebular/theme';
3
+import { RaidData, Character } from '../../../../../../backend/Types/Types';
4
+import { ApiService } from '../../services/login-api';
5
+import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
6
+
7
+@Component({
8
+    selector: 'characterpicker',
9
+    templateUrl: './characterpicker.component.html',
10
+})
11
+export class FrontcraftCharacerpickerComponent implements OnInit{
12
+
13
+    raid : RaidData
14
+    characters: Character[]
15
+
16
+    constructor(
17
+        protected dialogRef: NbDialogRef<FrontcraftCharacerpickerComponent>,
18
+        private api : ApiService,
19
+        private toast : NbToastrService
20
+    ){}
21
+
22
+    signup = async (character: Character, late: boolean) => {
23
+        const auth = this.api.getAuth()
24
+        const signup = this.api.get('signup')
25
+        if(!signup) return
26
+
27
+        await signup.sign(auth.token.value, character, this.raid, late)
28
+        this.toast.show('Signup', 'Success', { status: 'success' })
29
+        this.dialogRef.close()
30
+    }
31
+
32
+    ngOnInit(): void {
33
+        const usr = this.api.getCurrentUser()
34
+        this.api.get('CharacterManager')
35
+        .getCharactersOfUser(usr.username)
36
+        .then(chars => {
37
+            chars.forEach(char => {
38
+                char['color'] = getClassColor(char.class)
39
+            })
40
+            this.characters = chars
41
+        })
42
+    }
43
+
44
+}

+ 119
- 0
src/frontend/src/app/frontcraft/pages/raid/raid.component.html View File

1
+<nb-card class="col-12 col-xl-9">
2
+    <nb-card-body>
3
+        <nb-tabset>
4
+            <nb-tab tabTitle="Info">
5
+                <h1>{{raid.title}}</h1>
6
+                <p>
7
+                    {{raid.signupcount}} / {{raid.size}} signups
8
+                </p>
9
+                <p>
10
+                    {{raid.description}}
11
+                </p>
12
+                <div *ngIf="canSignup">
13
+                    <div *ngIf="isSignedup">
14
+
15
+                        <p>
16
+                            You are signed as: {{mySignup.charactername}} ({{mySignup.race}} {{mySignup.specname}} {{mySignup.class}})<br />
17
+                            Status: {{mySignup.status}}
18
+                        </p>
19
+                        
20
+                        <button
21
+                            (click)="unsign()"
22
+                            nbButton 
23
+                            outline 
24
+                            status="danger" 
25
+                            size="medium">
26
+                            unsign
27
+                        </button>                    
28
+                    </div>
29
+                    <div *ngIf="!isSignedup">
30
+                        <button
31
+                            (click)="signup()"
32
+                            nbButton 
33
+                            outline 
34
+                            status="success" 
35
+                            size="medium">
36
+                            signup
37
+                        </button>                     
38
+                    </div>
39
+                </div>
40
+            </nb-tab>
41
+            
42
+            <nb-tab tabTitle="Signups"
43
+              [badgeText]="raid.signupcount"
44
+              badgePosition="top right"
45
+              [badgeStatus]="raid.signupcount<40?'warning':'success'">
46
+                <div class="row">
47
+                    <ng-container *ngFor="let group of raid.participants | keyvalue">
48
+                        <nb-card
49
+                        class="col-12 col-md-6 col-xl-4" 
50
+                        *ngIf="group.value.length > 0">
51
+                            <nb-card-header>{{group.key}} ({{group.value.length}})</nb-card-header>
52
+                            <nb-card-body>
53
+                                <div *ngFor="let participant of group.value">
54
+                                    <button
55
+                                        (click)="setBench(participant)"
56
+                                        *ngIf="manageRaid" 
57
+                                        nbButton 
58
+                                        outline 
59
+                                        status="warning" 
60
+                                        size="tiny">
61
+                                        B
62
+                                    </button>
63
+                                    <a [ngStyle]="{'color': participant.color}" style="text-transform: capitalize;" 
64
+                                    [routerLink]="'/frontcraft/character/'+participant.charactername">
65
+                                    {{ participant.charactername }}
66
+                                    </a>
67
+                                </div>
68
+                            </nb-card-body>
69
+                        </nb-card>
70
+
71
+                    </ng-container>
72
+                </div>
73
+            </nb-tab>
74
+            <nb-tab tabTitle="Reserves">
75
+                <nb-list>
76
+                    <nb-list-item *ngFor="let item of raid.tokens | keyvalue">
77
+                        
78
+                        <a [ngStyle]="{'color':item.value[0].quality=='Epic'?'#a335ee':'#ff8000'}" 
79
+                           target="_blank" 
80
+                           [href]="item.value[0].url">
81
+                           <img style="min-width: 25px; width: 2.25vw; max-width: 50px" 
82
+                           [src]="'https://wow.zamimg.com/images/wow/icons/large/'+item.value[0].iconname+'.jpg'" />
83
+                           &nbsp;
84
+                           {{item.key}}
85
+                        </a><br />
86
+                        <div class="row">
87
+                            <div *ngFor="let token of item.value" class="col-12 col-md-6 col-xl-4">
88
+                                [ {{token.level}} ]  
89
+                                <span style="text-transform: capitalize;" [ngStyle]="{'color':token.level>=10?'#ff8000':token.level>=8?'#a335ee':token.level>=6?'#0070dd':token.level>=4?'#1eff00':token.level>=2?'#ffffff':'#9d9d9d'}">
90
+                                    {{token.charactername}}
91
+                                </span><br />
92
+                            </div>
93
+                        </div>
94
+                    </nb-list-item>
95
+                </nb-list>
96
+            </nb-tab>
97
+            <nb-tab tabTitle="Admin" *ngIf="manageRaid">
98
+                <button
99
+                    (click)="startRaid(raid)"
100
+                    nbButton 
101
+                    outline 
102
+                    status="success" 
103
+                    size="medium">
104
+                    start
105
+                </button>
106
+
107
+                <button
108
+                    (click)="archiveRaid(raid)"
109
+                    nbButton 
110
+                    outline 
111
+                    status="danger" 
112
+                    size="medium">
113
+                    archive
114
+                </button>
115
+            </nb-tab>
116
+        </nb-tabset>
117
+    </nb-card-body>
118
+</nb-card>
119
+

+ 123
- 0
src/frontend/src/app/frontcraft/pages/raid/raid.component.ts View File

1
+import { Component, OnInit } from '@angular/core';
2
+import { ActivatedRoute, Router } from '@angular/router';
3
+import { ApiService as ApiService } from '../../services/login-api';
4
+import { RaidData, Raid, Signup } from '../../../../../../backend/Types/Types';
5
+import { NbWindowService, NbToastrService, NbDialogService } from '@nebular/theme';
6
+import { FrontcraftCharacerpickerComponent } from './characterpicker.component';
7
+import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
8
+
9
+@Component({
10
+  selector: 'raid',
11
+  templateUrl: './raid.component.html',
12
+})
13
+export class FrontcraftRaidComponent implements OnInit{
14
+
15
+    canSignup = false
16
+    isSignedup = false
17
+    manageRaid
18
+    mySignup
19
+
20
+    raid: RaidData = <any>{
21
+      participants:{
22
+          Druid: [],
23
+          Hunter: [],
24
+          Mage: [],
25
+          Paladin: [],
26
+          Priest: [],
27
+          Rogue: [],
28
+          Shaman: [],
29
+          Warlock: [],
30
+          Warrior: [],
31
+      },
32
+      tokens:{}
33
+    }
34
+    
35
+    constructor(
36
+      private api: ApiService,
37
+      private route: ActivatedRoute,
38
+      private router: Router,
39
+      private dialogService : NbDialogService,
40
+      private toast: NbToastrService
41
+    ){
42
+      window['r'] = this
43
+    }
44
+
45
+    async ngOnInit(){
46
+      this.manageRaid = this.api.get('manageRaid')
47
+      
48
+      const signupFeature = this.api.get('signup')
49
+      if(signupFeature){
50
+        this.canSignup = true
51
+      }
52
+      this.refresh()
53
+    }
54
+
55
+    signup = async () => {
56
+      const signupFeature = this.api.get('signup')
57
+      if(!signupFeature) return
58
+
59
+      this.dialogService.open(FrontcraftCharacerpickerComponent, { 
60
+        closeOnBackdropClick: true,
61
+        closeOnEsc: true,
62
+        context: {
63
+          'raid': this.raid,
64
+        } 
65
+      }).onClose.subscribe(()=>{
66
+        this.refresh()
67
+      });
68
+    }
69
+
70
+    async archiveRaid(raid:Raid){
71
+      await this.manageRaid!.archiveRaid(raid)
72
+      this.toast.show('Raid archived', 'Success', { status: 'success' })
73
+      this.router.navigateByUrl('/frontcraft/archive/'+raid.id)
74
+    }
75
+
76
+    async startRaid(raid:Raid){
77
+      await this.manageRaid!.startRaid(raid)
78
+      this.toast.show('Raid started', 'Success', { status: 'success' })
79
+      this.router.navigateByUrl('/frontcraft/archive/'+raid.id)
80
+    }
81
+
82
+    unsign = async () => {
83
+      const signupFeature = this.api.get('signup')
84
+      if(!signupFeature) return
85
+
86
+      await signupFeature.unsign(this.api.getAuth().token.value, this.mySignup, this.raid)
87
+      this.toast.show('Success', 'Unsigned', { status: 'success' })
88
+      this.refresh()
89
+    }
90
+
91
+    refresh = async () => {
92
+      const param = this.route.snapshot.paramMap.get('id');
93
+
94
+      const raidManager = this.api.get('RaidManager')
95
+      const raiddata = await raidManager.getRaidData(<any>{
96
+        id: param
97
+      })
98
+      this.raid = raiddata
99
+
100
+      Object.values(raiddata.participants).flat().forEach(p => {
101
+        p['color'] = getClassColor(p.class)
102
+      })
103
+      const user = this.api.getCurrentUser()
104
+      const matchingSignup = Object.values(raiddata.participants).flat().find(char => user && char.userid === user.id!)
105
+      if(matchingSignup){
106
+        this.isSignedup = true
107
+        this.mySignup = matchingSignup
108
+        this.mySignup.status = matchingSignup['benched']?'Bench':matchingSignup['late']?'Late':'Attending'
109
+      }else{
110
+        this.isSignedup = false
111
+        this.mySignup = null
112
+      }
113
+    }
114
+
115
+    setBench(signup:Signup){
116
+      this.manageRaid.setBenched({
117
+        characterid: signup.characterid,
118
+        raidid: signup.raidid,
119
+        late: false,
120
+        benched: !signup.benched
121
+      }).then(_ => this.refresh())
122
+    }
123
+}

+ 28
- 0
src/frontend/src/app/frontcraft/pages/raids/createraid.component.html View File

1
+
2
+
3
+<nb-card class="col-12 col-xl-9">
4
+    <nb-card-header>
5
+      Create Raid
6
+    </nb-card-header>
7
+    <nb-select [(selected)]="template" placeholder="copy from" (selectedChange)="onTemplateSelect()">
8
+        <nb-option *ngFor="let template of templates" [value]="template">{{template.title}} {{template.start| date : 'd MMMM'}}</nb-option>
9
+    </nb-select>
10
+    
11
+    <input type="text" nbInput [(ngModel)]="title" placeholder="Title" fullWidth="true">
12
+    <input type="number" nbInput [(ngModel)]="size" placeholder="size" fullWidth="true">
13
+    <textarea nbInput fullWidth [(ngModel)]="description" placeholder="description" fullWidth="true"></textarea>
14
+    <input [nbDatepicker]="datepicker" [(ngModel)]="startdate">
15
+    <nb-datepicker #datepicker></nb-datepicker>
16
+    
17
+    <input type="number" nbInput [(ngModel)]="hour" placeholder="hour" fullWidth="true">
18
+    <input type="number" nbInput [(ngModel)]="minute" placeholder="minute" fullWidth="true">
19
+    
20
+    <button
21
+        (click)="submit()"
22
+        nbButton 
23
+        outline 
24
+        status="success" 
25
+        size="medium">
26
+        submit
27
+    </button>
28
+  </nb-card>

+ 59
- 0
src/frontend/src/app/frontcraft/pages/raids/createraid.compontent.ts View File

1
+import { Component, OnInit } from '@angular/core';
2
+import { ApiService } from '../../services/login-api';
3
+import { Raid, RaidData } from '../../../../../../backend/Types/Types';
4
+import { NbWindowRef, NbDialogRef } from '@nebular/theme';
5
+
6
+const ONE_MINUTE = 60000
7
+const ONE_HOUR = 60 * ONE_MINUTE
8
+
9
+@Component({
10
+  selector: 'createraid',
11
+  templateUrl: 'createraid.component.html',
12
+})
13
+export class FrontcraftCreateRaidsComponent implements OnInit {
14
+
15
+  templates :RaidData[]
16
+  template :RaidData
17
+
18
+  title = ""
19
+  startdate = new Date()
20
+  hour = 20
21
+  minute = 0
22
+  size = 40
23
+  description = ""
24
+
25
+  constructor(
26
+    protected dialogRef: NbDialogRef<FrontcraftCreateRaidsComponent>,
27
+    private api: ApiService
28
+  ) {}
29
+
30
+  loadNext(cardData) {
31
+  }
32
+
33
+  onTemplateSelect(){
34
+    const templateDate = new Date(this.template.start)
35
+    this.title = this.template.title
36
+    this.hour = templateDate.getHours()
37
+    this.minute = templateDate.getMinutes()
38
+    this.description = this.template.description
39
+    this.startdate = new Date(parseInt(this.template.start) - templateDate.getHours()*ONE_HOUR - templateDate.getMinutes()*ONE_MINUTE)
40
+  }
41
+
42
+  ngOnInit(){
43
+
44
+  }
45
+
46
+  async submit(){
47
+    const raid = <Raid>{
48
+      description: this.description,
49
+      size: this.size,
50
+      start: ""+new Date(this.startdate.getTime() + this.hour*ONE_HOUR + this.minute*ONE_MINUTE).getTime(),
51
+      title: this.title,
52
+    }
53
+    const manage = this.api.get('manageRaid')
54
+    if(!manage) return
55
+
56
+    await manage.createRaid(raid)
57
+    this.dialogRef.close()
58
+  }
59
+}

+ 37
- 0
src/frontend/src/app/frontcraft/pages/raids/raids.component.html View File

1
+<nb-card class="col-12 col-xl-9">
2
+  <nb-card-header>
3
+    Upcoming raids
4
+    <nb-icon 
5
+      style="color:orange"
6
+      *ngIf="manageRaid"
7
+      (click)="create()"
8
+      icon="plus-square-outline"></nb-icon>
9
+  </nb-card-header>
10
+  <nb-list
11
+    nbInfiniteList
12
+    listenWindowScroll
13
+    [threshold]="500">
14
+    <nb-list-item *ngFor="let raid of raids" [routerLink]="'/frontcraft/raid/'+raid.id">
15
+      <b>{{raid.title}}</b><br>
16
+      {{raid.signupcount}} / {{raid.size}}<br>
17
+      {{raid.start | date : 'EEEE d MMMM @ HH:mm'}}<br>
18
+      {{raid.description}}<br>
19
+    </nb-list-item>
20
+  </nb-list>
21
+</nb-card>
22
+
23
+<nb-card class="col-12 col-xl-9">
24
+  <nb-card-header>Previous raids</nb-card-header>
25
+  <nb-list
26
+    nbInfiniteList
27
+    listenWindowScroll
28
+    [threshold]="500">
29
+    <nb-list-item *ngFor="let raid of oldraids" [routerLink]="'/frontcraft/archive/'+raid.id">
30
+      <b>{{raid.title}}</b><br>
31
+      {{raid.signupcount}} / {{raid.size}}<br>
32
+      {{raid.start | date : 'EEEE d MMMM @ HH:mm'}}<br>
33
+      {{raid.description}}<br>
34
+    </nb-list-item>
35
+  </nb-list>
36
+</nb-card>
37
+  

+ 7
- 0
src/frontend/src/app/frontcraft/pages/raids/raids.component.scss View File

1
+.infinite-cards {
2
+  nb-card {
3
+    &.own-scroll {
4
+      height: 50vh;
5
+    }
6
+  }
7
+}

+ 54
- 0
src/frontend/src/app/frontcraft/pages/raids/raids.component.ts View File

1
+import { Component, OnInit } from '@angular/core';
2
+import { ApiService } from '../../services/login-api';
3
+import { NbWindowService, NbDialogService } from '@nebular/theme';
4
+import { FrontcraftCreateRaidsComponent } from './createraid.compontent';
5
+
6
+@Component({
7
+  selector: 'raids',
8
+  templateUrl: 'raids.component.html',
9
+  styleUrls: ['raids.component.scss'],
10
+})
11
+export class FrontcraftRaidsComponent implements OnInit{
12
+
13
+
14
+  manageRaid
15
+  raids = []
16
+  oldraids = []
17
+  pageSize = 10;
18
+
19
+  constructor(
20
+    private api: ApiService,
21
+    private dialogService: NbDialogService
22
+  ) {
23
+    this.manageRaid = this.api.get('manageRaid')
24
+
25
+  }
26
+
27
+  ngOnInit(): void {
28
+    this.refresh()
29
+  }
30
+
31
+  refresh() {
32
+    this.api.get('RaidManager').getRaids().then(raids => {
33
+      this.raids = raids
34
+    })
35
+
36
+    const raidManager = this.api.get('RaidManager')
37
+    raidManager.getPastRaids(10).then(raiddata => {
38
+      this.oldraids = raiddata
39
+    })
40
+  }
41
+
42
+  create(){
43
+    this.dialogService.open(FrontcraftCreateRaidsComponent, { 
44
+      closeOnBackdropClick: true,
45
+      closeOnEsc: true,
46
+      context: {
47
+        templates: this.oldraids
48
+      } 
49
+    }).onClose.subscribe(()=>{
50
+      this.refresh()
51
+    });
52
+
53
+  }
54
+}

+ 31
- 0
src/frontend/src/app/frontcraft/pages/rules/rules.component.html View File

1
+<nb-card class="col-12 col-xl-9">
2
+    <nb-card-body>
3
+        <nb-tabset>
4
+            <nb-tab tabTitle="Info">
5
+                Smart text here
6
+            </nb-tab>
7
+            <nb-tab *ngIf="managePriorities" tabTitle="priorities">
8
+                <nb-list>
9
+                    <nb-list-item *ngFor="let item of rules | keyvalue">
10
+                        <a [ngStyle]="{'color':item.value[0].quality=='Epic'?'#a335ee':'#ff8000'}" 
11
+                        target="_blank" 
12
+                        [href]="item.value[0].url">
13
+                        <img style="min-width: 25px; width: 2.25vw; max-width: 50px" 
14
+                        [src]="'https://wow.zamimg.com/images/wow/icons/large/'+item.value[0].iconname+'.jpg'" />
15
+                        &nbsp;
16
+                        {{item.key}}
17
+                        </a><br />
18
+
19
+                        <span *ngFor="let rule of item.value" [nbPopover]="templateRef" nbPopoverTrigger="hover">
20
+                            <ng-template #templateRef>
21
+                                <span style="color:white">{{rule.description}}</span>
22
+                            </ng-template>
23
+                            +{{rule.modifier}} {{rule.race}} <span [ngStyle]="{'color':rule.color}">{{rule.specname}} {{rule.class}}</span> <br />
24
+                        </span>
25
+                    </nb-list-item>
26
+                </nb-list>
27
+            </nb-tab>
28
+        </nb-tabset>
29
+    </nb-card-body>
30
+</nb-card>
31
+

+ 31
- 0
src/frontend/src/app/frontcraft/pages/rules/rules.component.ts View File

1
+import { Component, OnInit } from '@angular/core';
2
+import { ApiService as ApiService } from '../../services/login-api';
3
+import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
4
+
5
+@Component({
6
+  selector: 'rules',
7
+  templateUrl: './rules.component.html',
8
+})
9
+export class FrontcraftRulesComponent implements OnInit{
10
+
11
+    managePriorities
12
+    rules = {}
13
+
14
+    constructor(
15
+      private api: ApiService,
16
+    ){
17
+    }
18
+
19
+    async ngOnInit(){
20
+      this.api.get('ItemManager').getAllPriorities().then(prios => prios.forEach(prio => {
21
+        if(!this.rules[prio.itemname]){
22
+          this.rules[prio.itemname] = []
23
+        }
24
+        prio['color'] = getClassColor(prio.class) || '#FFFFFF'
25
+        this.rules[prio.itemname].push(prio)
26
+      }))
27
+      
28
+      const managePriorities = this.api.get('managePriorities')
29
+      this.managePriorities = managePriorities
30
+    }
31
+}

+ 26
- 0
src/frontend/src/app/frontcraft/pages/shop/itemselect.component.html View File

1
+
2
+
3
+    <input type="text" nbInput [(ngModel)]="search" (change)="changeSearch()" placeholder="Search" fullWidth="true">
4
+    <nb-list
5
+    nbInfiniteList
6
+    listenWindowScroll>
7
+        <nb-list-item *ngFor="let item of displayedItems">
8
+            <button
9
+                (click)="select(item)"
10
+                nbButton 
11
+                outline 
12
+                status="info" 
13
+                size="tiny">
14
+                select
15
+            </button>
16
+            &nbsp;
17
+            <a target="_blank" [href]="item.url">
18
+                <span [ngStyle]="{'color':item.quality=='Epic'?'#a335ee':'#ff8000'}">
19
+                    <img style="min-width: 20px; width: 2.25vw; max-width: 35px" 
20
+                    [src]="'https://wow.zamimg.com/images/wow/icons/large/'+item.iconname+'.jpg'" />
21
+                    &nbsp;
22
+                    {{item.itemname}}
23
+                </span>
24
+            </a>
25
+        </nb-list-item>
26
+    </nb-list>

+ 45
- 0
src/frontend/src/app/frontcraft/pages/shop/itemselector.component.ts View File

1
+import { Component, OnInit } from '@angular/core';
2
+import { ApiService } from '../../services/login-api';
3
+import { Item } from '../../../../../../backend/Types/Types';
4
+
5
+@Component({
6
+  selector: 'itemselect',
7
+  templateUrl: './itemselect.component.html',
8
+})
9
+export class FrontcraftItemSelectComponent implements OnInit{
10
+
11
+    selected: Item
12
+    search: string
13
+    items: any[] 
14
+    displayedItems: any[]
15
+
16
+    callbacks = []
17
+
18
+    constructor(
19
+      private api: ApiService,
20
+    ){
21
+    }
22
+
23
+    async ngOnInit(){
24
+        this.api.get('ItemManager').getItems().then(items => {
25
+            this.items = items
26
+            this.displayedItems = items
27
+        })
28
+    }
29
+
30
+    changeSearch(){
31
+        if(!this.search || this.search == "") 
32
+            this.displayedItems = this.items
33
+        else
34
+            this.displayedItems = this.items.filter(it => it.itemname.toLowerCase().includes(this.search.toLowerCase()))
35
+    }
36
+
37
+    select(item: Item){
38
+        this.selected = item
39
+        this.callbacks.forEach(cb => cb(item))
40
+    }
41
+
42
+    onselect(callback:Function){
43
+        this.callbacks.push(callback)
44
+    }
45
+}

+ 13
- 0
src/frontend/src/app/frontcraft/pages/shop/shop.component.html View File

1
+
2
+<nb-card class="col-12 col-xl-9">
3
+    <nb-card-body>
4
+        <nb-tabset>
5
+            <nb-tab tabTitle="Items">
6
+                <itemselect></itemselect>
7
+            </nb-tab>
8
+            <nb-tab tabTitle="About">
9
+            </nb-tab>
10
+        </nb-tabset>
11
+    </nb-card-body>
12
+</nb-card>
13
+

+ 135
- 0
src/frontend/src/app/frontcraft/pages/shop/shop.component.ts View File

1
+import { Component, OnInit, ContentChild, AfterContentInit, ViewChild, AfterViewInit } from '@angular/core';
2
+import { ApiService as ApiService } from '../../services/login-api';
3
+import { FrontcraftItemSelectComponent } from './itemselector.component';
4
+import { NbWindowService, NbWindowRef, NbToastrService, NbDialogService, NbDialogRef } from '@nebular/theme';
5
+import { Item, Character, SRToken } from '../../../../../../backend/Types/Types';
6
+import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
7
+
8
+@Component({
9
+  selector: 'shop',
10
+  templateUrl: './shop.component.html',
11
+})
12
+export class FrontcraftShopComponent implements AfterViewInit{
13
+
14
+    @ViewChild(FrontcraftItemSelectComponent, {static: false})
15
+    itemselect !: FrontcraftItemSelectComponent
16
+
17
+    constructor(
18
+      private api: ApiService,
19
+      private dialogService : NbDialogService
20
+
21
+    ){
22
+      window['shop'] = this
23
+    }
24
+
25
+    buy = (item) => {
26
+      
27
+      this.dialogService.open(FrontcraftBuyTokenComponent, { 
28
+        closeOnBackdropClick: true,
29
+        closeOnEsc: true,
30
+        context: {
31
+          item: item
32
+        } 
33
+      }).onClose.subscribe(()=>{
34
+      });
35
+  
36
+    }
37
+
38
+    ngAfterViewInit(){
39
+      this.itemselect.onselect(this.buy)
40
+    }
41
+}
42
+
43
+@Component({
44
+  selector: 'buyToken',
45
+  template: `
46
+  <nb-card class="col-12 col-xl-9">
47
+    <nb-card-header>
48
+      Buy {{item.itemname}}
49
+    </nb-card-header>
50
+    <nb-card-body>
51
+      <p>
52
+        You currently have {{currency}} softreserve currency
53
+      </p>  
54
+      <div *ngIf="currency>0">
55
+        <div *ngFor="let kv of modifier | keyvalue">
56
+          <button
57
+              (click)="buyToken(kv.key)"
58
+              [disabled]="currency<=0"
59
+              nbButton 
60
+              outline 
61
+              status="success" 
62
+              size="tiny">
63
+              buy
64
+          </button>
65
+          {{kv.key}} <span *ngIf="kv.value>0">(<b>+{{kv.value}}</b> from priorities)</span>
66
+        </div>
67
+        <div *ngFor="let token of ownedtokens">
68
+          <button
69
+              (click)="buyToken(token.charactername)"
70
+              nbButton 
71
+              outline 
72
+              status="success" 
73
+              size="tiny">
74
+              buy
75
+          </button>
76
+          {{token.charactername}} [ {{token.level}} ] => [ {{token.level+1}} ]
77
+        </div>
78
+      </div>
79
+    </nb-card-body>
80
+</nb-card>
81
+  
82
+  `,
83
+})
84
+export class FrontcraftBuyTokenComponent implements OnInit{
85
+
86
+  item : Item
87
+  characters: Character[]
88
+  modifier = {}
89
+  currency: number = 0
90
+  ownedtokens: SRToken[] = []
91
+
92
+  constructor(
93
+    private toastr: NbToastrService,
94
+    protected dialogRef: NbDialogRef<FrontcraftBuyTokenComponent>,
95
+    private api : ApiService
96
+  ){}
97
+
98
+  ngOnInit(): void {
99
+      const usr = this.api.getCurrentUser()
100
+      this.api.get('CharacterManager')
101
+      .getCharactersOfUser(usr.username)
102
+      .then(chars => {
103
+          chars.forEach(char => {
104
+              char['color'] = getClassColor(char.class)
105
+              this.api.get('ItemManager').getToken(char, this.item).then(token => {
106
+                if(token) this.ownedtokens.push(token)
107
+                else{
108
+                  this.api.get('ItemManager').calculatePriorities(this.item.itemname, char).then(modifier => {
109
+                    this.modifier[char.charactername] = modifier
110
+                  })
111
+                }
112
+              })
113
+          })
114
+          this.characters = chars
115
+      })
116
+      this.api.get('UserManager').getUser(usr.username).then(u => {
117
+        if(!u) return
118
+        this.currency = u.currency
119
+      })
120
+  }
121
+
122
+  buyToken = async (charactername:string) => {
123
+
124
+    const src = this.api.get('ItemManager')
125
+    const token = await src.buyToken(this.api.getAuth().token.value, charactername, this.item.itemname)
126
+    
127
+
128
+    if(token){
129
+      this.toastr.show(token.characterid+' now has a token for '+token.itemname+' of level '+token.level, 'Yay', {status: 'success'})
130
+    }else{
131
+      this.toastr.show('Error (something went wrong)', 'Oh no', {status: 'danger'})
132
+    }
133
+    this.dialogRef.close()
134
+  }
135
+}

+ 16
- 2
src/frontend/src/app/frontcraft/pages/user/user.component.html View File

1
 
1
 
2
 
2
 
3
-<nb-card
3
+<nb-card class="col-xl-9"
4
 accent="info">
4
 accent="info">
5
     <nb-card-header>
5
     <nb-card-header>
6
         <h2 style="text-transform: capitalize;">{{user.username}}</h2>
6
         <h2 style="text-transform: capitalize;">{{user.username}}</h2>
7
     </nb-card-header>
7
     </nb-card-header>
8
     <nb-card-body>
8
     <nb-card-body>
9
+        {{user.currency}}
9
       <nb-card
10
       <nb-card
10
       accent="control"
11
       accent="control"
11
       *ngFor="let char of characters">
12
       *ngFor="let char of characters">
17
               </h4>
18
               </h4>
18
           </nb-card-header>
19
           </nb-card-header>
19
           <nb-card-body>
20
           <nb-card-body>
20
-              {{char.specname}} {{char.class}}
21
+            {{char.race}}<br />
22
+            {{char.specname}} {{char.class}}
23
+            <br />
24
+            <br />
25
+            <span *ngFor="let token of char.tokens">
26
+                [ {{token.level}} ]
27
+                <a [ngStyle]="{'color':token.quality=='Epic'?'#a335ee':'#ff8000'}" 
28
+                target="_blank" 
29
+                [href]="token.url">
30
+                <img style="min-width: 25px; width: 2.25vw; max-width: 50px" 
31
+                [src]="'https://wow.zamimg.com/images/wow/icons/large/'+token.iconname+'.jpg'" />
32
+                {{token.itemname}}
33
+                </a><br />
34
+            </span>
21
           </nb-card-body>
35
           </nb-card-body>
22
       </nb-card>
36
       </nb-card>
23
     </nb-card-body>
37
     </nb-card-body>

+ 19
- 20
src/frontend/src/app/frontcraft/pages/user/user.component.ts View File

1
 import { Component, OnInit } from '@angular/core';
1
 import { Component, OnInit } from '@angular/core';
2
-import { LoginApiService } from '../../services/login-api';
3
-import { Router } from '@angular/router';
2
+import { ApiService } from '../../services/login-api';
3
+import { Router, ActivatedRoute } from '@angular/router';
4
 import { User, Spec, Character } from '../../../../../../backend/Types/Types';
4
 import { User, Spec, Character } from '../../../../../../backend/Types/Types';
5
 import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
5
 import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
6
 
6
 
14
   characters : (Character & Spec)[] = []
14
   characters : (Character & Spec)[] = []
15
 
15
 
16
   constructor(
16
   constructor(
17
-    private router : Router,  
18
-    private loginApi : LoginApiService
17
+    private route: ActivatedRoute,
18
+    private api : ApiService
19
   ){}
19
   ){}
20
 
20
 
21
-  ngOnInit(){
22
-    const auth = this.loginApi.getAuth()
23
-    if(!auth){
24
-      this.router.navigateByUrl('/auth/login')
25
-      return
26
-    }
27
-    this.user = auth.user
28
-    this.loginApi.getUnprivilegedSocket()
29
-    .CharacterManager
30
-    .getCharactersOfUser(this.user.username)
31
-    .then((characters) => {
32
-      characters.forEach(c => {
33
-        c['color'] = getClassColor(c.class)
34
-      })
35
-      this.characters = characters
36
-    })  
21
+  async ngOnInit(){
22
+    const param = this.route.snapshot.paramMap.get('name');
23
+    const usr = await this.api.get('UserManager').getUser(param)
24
+
25
+
26
+    if(!usr) return
27
+    this.user = usr
28
+
29
+    const characters = await this.api.get('CharacterManager').getCharactersOfUser(this.user.username)
30
+    characters.forEach(async c => {
31
+      const tokens = await this.api.get('ItemManager').getTokens(c)
32
+      c['tokens'] = tokens
33
+      c['color'] = getClassColor(c.class)
34
+    })
35
+    this.characters = characters
37
   }
36
   }
38
 }
37
 }

+ 26
- 15
src/frontend/src/app/frontcraft/services/login-api.ts View File

1
 import { Injectable, Injector, NgZone } from "@angular/core";
1
 import { Injectable, Injector, NgZone } from "@angular/core";
2
 import {RPCSocket} from 'rpclibrary/js/src/Frontend'
2
 import {RPCSocket} from 'rpclibrary/js/src/Frontend'
3
 
3
 
4
-import { Token, Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../backend/Types/Types'
4
+import { Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../backend/Types/Types'
5
 import { CookieService } from 'ngx-cookie-service';
5
 import { CookieService } from 'ngx-cookie-service';
6
 import { Router } from '@angular/router';
6
 import { Router } from '@angular/router';
7
+import { ShoutMessage } from '../../../../../backend/Components/Shoutbox/Interface';
7
 
8
 
8
 @Injectable()
9
 @Injectable()
9
-export class LoginApiService{
10
+export class ApiService{
10
     private socket:RPCSocket & FrontcraftIfc;
11
     private socket:RPCSocket & FrontcraftIfc;
11
     private auth:Auth
12
     private auth:Auth
12
     private privSocket: RPCSocket & SomeOf<FrontcraftFeatureIfc>
13
     private privSocket: RPCSocket & SomeOf<FrontcraftFeatureIfc>
37
             sock.hook('getUserData', () => auth)
38
             sock.hook('getUserData', () => auth)
38
             sock.hook('navigate', (where:string) => {
39
             sock.hook('navigate', (where:string) => {
39
                 this.ngZone.run( () => {
40
                 this.ngZone.run( () => {
40
-                    this.injector.get(Router).navigateByUrl('/')
41
+                    this.injector.get(Router).navigateByUrl(where)
41
                 })
42
                 })
42
             })
43
             })
43
             sock.on('error', (e) => {
44
             sock.on('error', (e) => {
55
         }
56
         }
56
     }
57
     }
57
 
58
 
58
-    getFeature = async <K extends keyof FrontcraftFeatureIfc>(feature : K) : Promise<void | FrontcraftFeatureIfc[K]> => {
59
+    get = <K extends (keyof FrontcraftIfc | keyof FrontcraftFeatureIfc)>(feature : K) : K extends keyof FrontcraftIfc?FrontcraftIfc[K]:
60
+                                                                                        K extends keyof FrontcraftFeatureIfc?(FrontcraftFeatureIfc[K] | void): 
61
+                                                                                        never => {
59
         try{
62
         try{
60
-            const sock = await this.getPrivilegedSocket(this.auth)
61
-            if(sock[feature]) return <FrontcraftFeatureIfc[K]> sock[feature]
63
+            //@ts-ignore
64
+            if(this.socket && this.socket[feature]) return this.socket[feature]
65
+            //@ts-ignore
66
+            if(this.privSocket && this.privSocket[feature]) return this.privSocket[feature]
62
         }catch(e){
67
         }catch(e){
63
             return
68
             return
64
-        }
69
+        } 
65
     }
70
     }
66
 
71
 
67
     getCurrentUser = () : User | undefined => {
72
     getCurrentUser = () : User | undefined => {
70
 
75
 
71
     login = async (username: string, password: string) : Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>> => {
76
     login = async (username: string, password: string) : Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>> => {
72
         const pwHash = await hash(password)
77
         const pwHash = await hash(password)
73
-        const auth = await this.socket.Authenticator.login(username, pwHash)
78
+        const auth = await this.socket.UserManager.login(username, pwHash)
74
 
79
 
75
         if(!auth){ 
80
         if(!auth){ 
76
             await this.logout()
81
             await this.logout()
80
         return sock
85
         return sock
81
     }
86
     }
82
 
87
 
88
+    async connectShoutbox(handler) : Promise<Function>{
89
+        const res = await this.get('Shoutbox').subscribe(handler)
90
+        return async (msg: ShoutMessage) => await this.get('Shoutbox').shout(res.uuid, msg)
91
+    }
92
+
83
     logout = async () => {
93
     logout = async () => {
84
         this.cookieSvc.set('token', undefined)
94
         this.cookieSvc.set('token', undefined)
85
-        if(this.auth) await this.socket.Authenticator.logout(this.auth.user.username, this.auth.token.value)
95
+        if(this.auth) await this.socket.UserManager.logout(this.auth.user.username, this.auth.token.value)
86
         if(this.privSocket) this.privSocket.destroy()
96
         if(this.privSocket) this.privSocket.destroy()
87
         this.privSocket = null
97
         this.privSocket = null
88
         this.auth  = null
98
         this.auth  = null
91
         })
101
         })
92
     }
102
     }
93
 
103
 
94
-
95
     initialize = async () : Promise<any> => {
104
     initialize = async () : Promise<any> => {
96
         const sock = await RPCSocket.makeSocket<FrontcraftIfc>(20000, window.location.hostname)
105
         const sock = await RPCSocket.makeSocket<FrontcraftIfc>(20000, window.location.hostname)
106
+
97
         this.socket = sock
107
         this.socket = sock
98
         try{
108
         try{
99
             const cookie = JSON.parse(this.cookieSvc.get('token'))
109
             const cookie = JSON.parse(this.cookieSvc.get('token'))
110
+            
100
             if(cookie != null) {
111
             if(cookie != null) {
101
                 try{
112
                 try{
102
-                    const auth = await sock.Authenticator.getAuth(cookie.token.value)
113
+                    const auth = await sock.UserManager.getAuth(cookie.token.value)
103
                     if(!auth) return sock
114
                     if(!auth) return sock
104
                     return await this.getPrivilegedSocket(auth)
115
                     return await this.getPrivilegedSocket(auth)
105
                 }catch(e){
116
                 }catch(e){
115
 
126
 
116
     async checkLogin() : Promise<boolean>{
127
     async checkLogin() : Promise<boolean>{
117
         if(!this.auth) return false
128
         if(!this.auth) return false
118
-        const valid = await this.socket.Authenticator.checkToken(this.auth.token.value, this.auth.user.rank)
129
+        const valid = this.socket.UserManager.checkToken(this.auth.token.value, this.auth.user.rank)
119
         if(valid) return true
130
         if(valid) return true
120
         await this.logout()
131
         await this.logout()
121
         return false
132
         return false
126
     return new Buffer(str)
137
     return new Buffer(str)
127
   }
138
   }
128
   
139
   
129
-function buf2hex(buffer) { // buffer is an ArrayBuffer
140
+function buf2hex(buffer: ArrayBuffer) {
130
     return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
141
     return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
131
 }
142
 }
132
 
143
 
137
 }
148
 }
138
 
149
 
139
 //angular depenency manager requires this
150
 //angular depenency manager requires this
140
-export function initializeLoginSvc(svc: LoginApiService): () => Promise<any> {
141
-    return svc.initialize
151
+export function initializeLoginSvc(svc: ApiService): () => Promise<any> {
152
+    return ()=>svc.initialize?svc.initialize():undefined
142
 }
153
 }

+ 1
- 1
src/frontend/tsconfig.json View File

2
   "compileOnSave": false,
2
   "compileOnSave": false,
3
   "compilerOptions": {
3
   "compilerOptions": {
4
     "importHelpers": true,
4
     "importHelpers": true,
5
-    "module": "esnext",
5
+    "module": "commonjs",
6
     "outDir": "./dist/out-tsc",
6
     "outDir": "./dist/out-tsc",
7
     "sourceMap": true,
7
     "sourceMap": true,
8
     "declaration": false,
8
     "declaration": false,

Loading…
Cancel
Save