浏览代码

work

master
peter 5 年前
父节点
当前提交
5cf7a25e77
共有 75 个文件被更改,包括 2244 次插入758 次删除
  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 查看文件

@@ -714,9 +714,9 @@
714 714
       "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
715 715
     },
716 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 721
     "balanced-match": {
722 722
       "version": "1.0.0",
@@ -1166,6 +1166,11 @@
1166 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 1174
     "class-utils": {
1170 1175
       "version": "0.3.6",
1171 1176
       "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@@ -1465,6 +1470,11 @@
1465 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 1478
     "cyclist": {
1469 1479
       "version": "0.2.2",
1470 1480
       "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
@@ -5031,17 +5041,26 @@
5031 5041
       "dev": true
5032 5042
     },
5033 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 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 5060
       "requires": {
5043 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 5066
     "npm-run-path": {
@@ -5752,9 +5771,9 @@
5752 5771
       "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
5753 5772
     },
5754 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 5778
     "public-encrypt": {
5760 5779
       "version": "4.0.3",
@@ -6111,9 +6130,9 @@
6111 6130
       }
6112 6131
     },
6113 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 6136
       "requires": {
6118 6137
         "bsock": "^0.1.9",
6119 6138
         "http": "0.0.0",
@@ -6518,9 +6537,9 @@
6518 6537
       "dev": true
6519 6538
     },
6520 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 6543
       "requires": {
6525 6544
         "nan": "^2.12.1",
6526 6545
         "node-pre-gyp": "^0.11.0",

+ 4
- 2
package.json 查看文件

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

+ 11
- 5
src/backend/Admin/Admin.ts 查看文件

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

+ 1
- 1
src/backend/Admin/Interface.ts 查看文件

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

+ 45
- 34
src/backend/Components/Character/CharacterManager.ts 查看文件

@@ -1,15 +1,14 @@
1 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 4
 import { CharacterManagerIfc } from "./RPCInterface";
5 5
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
6 6
 import { getSpecTableData, SpecT } from "../../Types/PlayerSpecs";
7 7
 import { IAdmin } from "../../Admin/Interface";
8
-import { ILoginManager } from "../Login/Interface";
8
+import { IUserManager } from "../User/Interface";
9 9
 import { ICharacterManager } from "./Interface";
10
-import { getLogger } from "log4js";
11 10
 
12
-@Module(ICharacterManager)
11
+@Injectable(ICharacterManager)
13 12
 export class CharacterManager
14 13
 implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterManager{
15 14
     name = "CharacterManager" as "CharacterManager";   
@@ -17,35 +16,20 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
17 16
     @Inject(IAdmin)
18 17
     private admin: IAdmin   
19 18
 
20
-    @Inject(ILoginManager)
21
-    private loginManager : ILoginManager
19
+    @Inject(IUserManager)
20
+    private loginManager : IUserManager
22 21
 
23 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 32
     exportRPCFeatures = () => [
40
-        {
41
-            name: "createCharacter",
42
-            exportRPCs: () => [
43
-                {
44
-                    name: 'createCharacter',
45
-                    call: this.createCharacter
46
-                }
47
-            ]
48
-        }
49 33
     ]
50 34
     
51 35
     getTableDefinitions = (): TableDefiniton[] => [
@@ -58,6 +42,8 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
58 42
                 table.foreign("specid").references("specs.id")
59 43
                 table.integer("userid").notNullable()
60 44
                 table.foreign("userid").references("users.id")
45
+                table.string("race").notNullable()
46
+                table.boolean('alt').defaultTo(false)
61 47
             }
62 48
         },{
63 49
             name: 'specs',
@@ -87,13 +73,16 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
87 73
             return char
88 74
         }catch(e){
89 75
             console.log(e);
90
-            
91 76
         }
92 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 88
     getCharacterByName = async(charactername: string) : Promise<(Character & User & Spec) | void> => {
@@ -101,7 +90,7 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
101 90
         return await this.admin.knex('characters as c')
102 91
         .join('specs as s', 's.id', '=', 'c.specid')
103 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 94
         .where('charactername', '=', charactername)
106 95
         .first()
107 96
     }
@@ -111,10 +100,16 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
111 100
         return await this.admin.knex('characters as c')
112 101
         .join('users as u', 'u.id', '=', 'c.userid')
113 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 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 113
     getSpecId = async <c extends keyof SpecT>(clazz: c, name: SpecT[c]) => await this.admin.knex
119 114
     .from('specs')
120 115
     .select('id')
@@ -125,4 +120,20 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
125 120
     .first()
126 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 查看文件

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

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

+ 0
- 34
src/backend/Components/Debugger/Debugger.ts 查看文件

@@ -1,34 +0,0 @@
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 查看文件

@@ -1,5 +1,5 @@
1 1
 import { ConfigLoader } from "loadson";
2
-import { Inject, Module } from "../../Injector/ServiceDecorator";
2
+import { Inject, Injectable } from "../../Injector/ServiceDecorator";
3 3
 import { GuildManagerFeatureIfc, GuildManagerIfc } from "./RPCInterface";
4 4
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
5 5
 import { _Rank, Rank } from "../../Types/Types";
@@ -10,9 +10,9 @@ export type Guild = {
10 10
     name: string
11 11
     realm: string
12 12
     description: string
13
-}
13
+};
14 14
 
15
-@Module(IGuildManager)
15
+@Injectable(IGuildManager)
16 16
 export class GuildManager
17 17
 implements FrontworkComponent<GuildManagerIfc, GuildManagerFeatureIfc>, IGuildManager{
18 18
 
@@ -32,26 +32,18 @@ implements FrontworkComponent<GuildManagerIfc, GuildManagerFeatureIfc>, IGuildMa
32 32
         }
33 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 40
     exportRPCFeatures = () => [{
44 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 49
     getTableDefinitions = () => []

+ 10
- 2
src/backend/Components/Item/Interface.ts 查看文件

@@ -1,6 +1,14 @@
1
-import { Item } from "../../Types/Types"
1
+import { Item, Character, SRToken, SRPriority, Spec } from "../../Types/Types"
2 2
 
3 3
 export class IItemManager{
4 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 查看文件

@@ -1,19 +1,21 @@
1 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 3
 import { ItemManagerFeatureIfc, ItemManagerIfc } from "./RPCInterface";
5 4
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
6 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 7
 import { IAdmin } from "../../Admin/Interface";
9 8
 import { IItemManager } from "./Interface";
10 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 14
 const fetch = require('node-fetch')
13 15
 const xml2js = require('xml2js');
14 16
 const parser = new xml2js.Parser(/* options */);
15 17
 
16
-@Module(IItemManager)
18
+@Injectable(IItemManager)
17 19
 export class ItemManager
18 20
 implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefinitionExporter, IItemManager{
19 21
     name = "ItemManager" as "ItemManager";    
@@ -21,23 +23,49 @@ implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefin
21 23
     @Inject(IAdmin)
22 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 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 69
         const res = await fetch('https://classic.wowhead.com/item='+name+'&xml'); 
42 70
         const txt = await res.text(); 
43 71
         const r = await parser.parseStringPromise(txt); 
@@ -60,11 +88,24 @@ implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefin
60 88
             {
61 89
                 name: 'tokens',
62 90
                 tableBuilder: (table) => {
63
-                    table.integer("characterid").primary()
91
+                    table.primary(['characterid', 'itemid'])
92
+                    table.integer("characterid")
64 93
                     table.foreign("characterid").references("id").inTable('characters')
65
-                    table.integer("itemid").primary()
94
+                    table.integer("itemid")
66 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 111
                 name: 'items',
@@ -79,6 +120,126 @@ implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefin
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 243
     countItems = async() :Promise<number> => {
83 244
         const count = await this.admin.knex('items').count('*');
84 245
         return <number>count[0]['count(*)']
@@ -86,15 +247,15 @@ implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefin
86 247
 
87 248
     private initialized = false
88 249
     initialize = async () => {
89
-        if(!this.initialized)
90
-            this.initialized = true        
250
+        if(this.initialized) return
251
+        this.initialized = true        
91 252
         
92 253
         const allItems = [...T1]
93 254
         getLogger('ItemManager').debug('Checking items')
94 255
 
95 256
         const countCache = await this.countItems()
96 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 259
             try{
99 260
                 await this.admin
100 261
                 .knex('items')

+ 13
- 1
src/backend/Components/Item/RPCInterface.ts 查看文件

@@ -2,10 +2,22 @@ import { IItemManager } from "./Interface"
2 2
 
3 3
 export type ItemManagerIfc = {
4 4
     ItemManager: {
5
-        getItem: IItemManager['getItem']
5
+        getItem: IItemManager['fetchItem']
6 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 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 查看文件

@@ -1,18 +0,0 @@
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 查看文件

@@ -18,25 +18,12 @@ implements RPCExporter<ConfigLoaderIfc<ConfT>, "Config">{
18 18
 
19 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 查看文件

@@ -1,4 +1,4 @@
1
-import { Raid, Signup, Character } from "../../Types/Types"
1
+import { Raid, Signup, Character, RaidData } from "../../Types/Types"
2 2
 
3 3
 export class IRaidManager{
4 4
     getRaids: () => Promise<Raid[]>
@@ -6,5 +6,12 @@ export class IRaidManager{
6 6
     addSignup: (signup: Signup) => Promise<any>
7 7
     removeSignup: (signup: Signup) => Promise<any>    
8 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 查看文件

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

+ 237
- 39
src/backend/Components/Raid/RaidManager.ts 查看文件

@@ -1,12 +1,13 @@
1
-import { Inject, Module } from "../../Injector/ServiceDecorator";
1
+import { Inject, Injectable } from "../../Injector/ServiceDecorator";
2 2
 import { RaidManagerIfc, RaidManagerFeatureIfc } from "./RPCInterface";
3 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 5
 import { IAdmin } from "../../Admin/Interface";
6 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 11
 export class RaidManager
11 12
 implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManager{
12 13
     name = "RaidManager" as "RaidManager";    
@@ -14,37 +15,38 @@ implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManag
14 15
     @Inject(IAdmin)
15 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 31
     exportRPCFeatures() {
26 32
         return [{
27 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 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 52
     getTableDefinitions(): TableDefiniton[] {
@@ -56,16 +58,24 @@ implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManag
56 58
                     table.dateTime('start').notNullable()
57 59
                     table.string('description').notNullable()
58 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 70
                 name: 'signups',
63 71
                 tableBuilder: (table) => {
64 72
                     table.primary(['raidid', 'characterid'])
65 73
                     table.integer('raidid')
66
-                    table.foreign('raidid').references('id').inTable('raids')
74
+                    table.foreign('raidid').references('id').inTable('raids').onDelete('CASCADE')
67 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,9 +97,154 @@ implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManag
87 97
     })
88 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 249
     getSignups = async (raid:Raid) : Promise<Signup[]> => await this.admin
95 250
     .knex('signups')
@@ -99,17 +254,60 @@ implements FrontworkComponent<RaidManagerIfc, RaidManagerFeatureIfc>, IRaidManag
99 254
     .select('*')
100 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 259
         if(!maybeUserRecord || maybeUserRecord.user.id != character.userid){
105 260
             throw new Error("Bad Usertoken")
106 261
         }
107 262
 
108
-        await this.admin
263
+        const exists = await this.admin
109 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 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 查看文件

@@ -0,0 +1,14 @@
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 查看文件

@@ -0,0 +1,13 @@
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 查看文件

@@ -0,0 +1,64 @@
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 查看文件

@@ -1,6 +1,6 @@
1 1
 import { Auth, Rank, User, RPCPermission, UserRecord } from "../../Types/Types"
2 2
 
3
-export class ILoginManager{
3
+export class IUserManager{
4 4
     login: (username:string, pwHash:string) => Promise<Auth>
5 5
     logout: (username: string, tokenValue :string) => Promise<void>
6 6
     getAuth: (tokenValue: string) => Promise<Auth | void>
@@ -9,4 +9,12 @@ export class ILoginManager{
9 9
     getPermissions: () => Promise<RPCPermission[]>
10 10
     checkToken: (token: string, rank: Rank) => boolean
11 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 查看文件

@@ -0,0 +1,26 @@
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 查看文件

@@ -1,32 +1,34 @@
1 1
 import { RPCServer, Socket } from "rpclibrary";
2
-import { Inject, Module } from "../../Injector/ServiceDecorator";
2
+import { Inject, Injectable } from "../../Injector/ServiceDecorator";
3 3
 import { FrontworkAdmin } from "../../Admin/Admin";
4 4
 import { GuildManager } from "../Guild/GuildManager";
5 5
 import { ItemManager } from "../Item/ItemManager";
6 6
 import { RaidManager } from "../Raid/RaidManager";
7 7
 import { CharacterManager } from "../Character/CharacterManager";
8
-import { LoginManagerIfc, LoginManagerFeatureIfc } from "./RPCInterface";
8
+import { UserManagerFeatureIfc, UserManagerIfc } from "./RPCInterface";
9 9
 import { FrontworkComponent } from "../../Types/FrontworkComponent";
10 10
 import { Rank, User, Auth, _Rank, TableDefiniton, RPCPermission, FrontcraftFeatureIfc, AnyRPCExporter, Token, UserRecord } from "../../Types/Types";
11 11
 import { IAdmin } from "../../Admin/Interface";
12
-import { ILoginManager } from "./Interface";
12
+import { IUserManager } from "./Interface";
13 13
 import { getLogger, Logger } from "log4js";
14
+import { saltedHash } from "../../Util/hash";
14 15
 
15 16
 const uuid = require('uuid/v4')
16 17
 
18
+const salt = "6pIbc6yjSN"
17 19
 const ONE_WEEK = 604800000 
18 20
 
19 21
 type Serverstate = {
20 22
     server: RPCServer,
21 23
     port : number,
22 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 33
     @Inject(IAdmin)
32 34
     private admin: FrontworkAdmin
@@ -48,37 +50,44 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
48 50
     userLogins : {[username in string] : UserRecord} = {}
49 51
 
50 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 92
     getTableDefinitions = (): TableDefiniton[] => [
84 93
         {
@@ -89,7 +98,7 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
89 98
                 table.string("pwhash").notNullable()
90 99
                 table.string("rank").notNullable()
91 100
                 table.string("email").nullable().unique()
92
-                table.boolean("locked").defaultTo(true)
101
+                table.integer("currency").defaultTo(1)
93 102
             }
94 103
         },{
95 104
             name: 'rpcpermissions',
@@ -109,19 +118,19 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
109 118
     initialize = async () => {
110 119
         this.exporters = [this.guild, this.item, this.raid, this.character] 
111 120
         //set up permissions
112
-        getLogger('LoginManager').debug('inserting permissions')
121
+        getLogger('UserManager').debug('inserting permissions')
113 122
 
114 123
         await Promise.all( 
115 124
             [this, ...this.exporters].flatMap(exp => exp.exportRPCFeatures().map(async (feature) => {
116 125
             try{
117 126
                 await this.admin.knex.insert({ rpcname: feature.name }).into('rpcpermissions')
118 127
             }catch(e){
119
-                console.log(e);
128
+                getLogger('UserManager').debug(feature.name);
120 129
             }
121 130
         })))
122 131
 
123 132
         //start rankServers
124
-        getLogger('LoginManager').debug('Starting rank servers')
133
+        getLogger('UserManager').debug('Starting rank servers')
125 134
 
126 135
         let rankServers = { } as any
127 136
         await Promise.all(_Rank.map(async (r,i) => {
@@ -148,7 +157,7 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
148 157
                 try{
149 158
                     //return await state.server.destroy()
150 159
                 }catch(e){
151
-                    getLogger('LoginManager').warn(e)
160
+                    getLogger('UserManager').warn(e)
152 161
                 }
153 162
             })
154 163
         );
@@ -171,7 +180,7 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
171 180
         while(!data){
172 181
             tries ++
173 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 184
                 socket.destroy()
176 185
                 return false
177 186
             }
@@ -189,7 +198,7 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
189 198
 
190 199
     setPermission = async (permission: RPCPermission) => {
191 200
         await this.admin.knex('rpcpermissions')
192
-        .where('rpcname', '=', permission.rpcnamename)
201
+        .where('rpcname', '=', permission.rpcname)
193 202
         .update(permission)
194 203
     }
195 204
 
@@ -198,13 +207,14 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
198 207
     }
199 208
 
200 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 211
             .select(rank)
203 212
             .from('rpcpermissions')
204 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 220
     getRPCForRank = async (rank: Rank): Promise<AnyRPCExporter[]> => {
@@ -231,32 +241,33 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
231 241
             if(admins.length > 0){
232 242
                 return {} as User
233 243
             }
234
-
235
-            user.locked = false
236 244
         }
237 245
         user.username = user.username.toLowerCase()
246
+        user.pwhash = await saltedHash(user.pwhash, salt)
238 247
 
239 248
         await this.admin.knex('users')
240 249
         .insert(user)
241 250
 
242
-        const users = await this.admin.knex
251
+        const userRecord = await this.admin.knex
243 252
         .select("*")
244 253
         .from('users')
245 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 268
     logout = async (username:string, tokenValue : string) : Promise<void> => {
251 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 272
             if(this.userLogins[username]){
262 273
                 await Promise.all (Object.values(this.userLogins[username].connections).map(async (sock) => {
@@ -275,15 +286,22 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
275 286
         }
276 287
     }
277 288
 
289
+    wipeCurrency = async () => {
290
+        await this.admin.knex('users')
291
+        .update({currency: 0})
292
+    }
293
+
278 294
     login = async(username:string, pwHash:string) : Promise<Auth> => {
279 295
         username = username.toLowerCase()
280
-        const res:User[] = await this.admin.knex
296
+        const user:User = await this.admin.knex
281 297
         .select('*')
282 298
         .from('users')
283 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 305
             delete user.pwhash
288 306
 
289 307
             //return existing auth
@@ -339,11 +357,11 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
339 357
                                 }
340 358
                             }).catch((e) => {
341 359
                                 socket.destroy();
342
-                                getLogger('LoginManager').warn(e);
360
+                                getLogger('UserManager').warn(e);
343 361
                             })
344 362
                         },
345 363
                         errorHandler: (socket, e, rpcName, args) => {
346
-                            console.log(rpcName, args);
364
+                            getLogger('UserManager').error(rpcName, args, e);
347 365
                         },
348 366
                         sesame: (sesame) => this.checkToken(sesame, rank)
349 367
                     })
@@ -352,7 +370,7 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
352 370
                 new Promise((res, rej) => setTimeout(res, 500))
353 371
             ])
354 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 375
         return rpcServer
358 376
     }
@@ -360,6 +378,18 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
360 378
     checkToken = (token: string, rank: Rank) : boolean => this.rankServers[rank].allowed.includes(token)
361 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 393
     createToken = (user:User): Token => {
364 394
         
365 395
         if(this.userLogins[user.username]){
@@ -374,4 +404,49 @@ implements FrontworkComponent<LoginManagerIfc, LoginManagerFeatureIfc>, ILoginMa
374 404
 
375 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 查看文件

@@ -51,8 +51,6 @@ export const Injector = new class {
51 51
       return rootobj
52 52
     }
53 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 查看文件

@@ -6,10 +6,10 @@ import { FrontworkComponent } from "../Types/FrontworkComponent";
6 6
  * @returns {GenericClassDecorator<Type<any>>}
7 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 10
   return (target: Type<any>) => {
11 11
     Injector.modules.push({
12
-      implements: ifc,
12
+      implements: _interface,
13 13
       implementation: target
14 14
     })
15 15
   }
@@ -20,12 +20,12 @@ export const Module = (ifc?: Type<any>) : GenericClassDecorator<Type<any>> => {
20 20
  * @constructor
21 21
  */
22 22
 export const RootComponent = (config : {
23
-    implements : Type<any>
24
-    imports : Type<FrontworkComponent>[]
23
+    injectable : Type<any>
24
+    injects : Type<FrontworkComponent>[]
25 25
   }) : GenericClassDecorator<Type<any>> => {
26 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 29
     Injector.root = target
30 30
   }
31 31
 }

+ 46
- 7
src/backend/Types/Types.ts 查看文件

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

+ 7
- 0
src/backend/Util/hash.ts 查看文件

@@ -0,0 +1,7 @@
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 查看文件

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

+ 43
- 0
src/frontend/src/app/@theme/components/header/chat.component.ts 查看文件

@@ -0,0 +1,43 @@
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 查看文件

@@ -3,16 +3,27 @@
3 3
     <a (click)="toggleSidebar()" href="#" class="sidebar-toggle">
4 4
       <nb-icon icon="menu-2-outline"></nb-icon>
5 5
     </a>
6
-    <a class="logo" href="#" (click)="navigateHome()">Frontcraft</a>
6
+    <a class="logo" href="#" (click)="navigateHome()">{{title}}</a>
7 7
   </div>
8 8
 </div>
9 9
 
10 10
 <div class="header-container">
11 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 27
     <nb-action class="user-action">
17 28
       <nb-user [nbContextMenu]="userMenu"
18 29
                [onlyPicture]="false"

+ 48
- 22
src/frontend/src/app/@theme/components/header/header.component.ts 查看文件

@@ -1,11 +1,14 @@
1 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 4
 import { LayoutService } from '../../../@core/utils';
5 5
 import { map, takeUntil } from 'rxjs/operators';
6 6
 import { Subject } from 'rxjs';
7
-import { LoginApiService } from '../../../frontcraft/services/login-api';
7
+import { ApiService } from '../../../frontcraft/services/login-api';
8 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 13
 @Component({
11 14
   selector: 'ngx-header',
@@ -15,8 +18,8 @@ import { User } from '../../../../../../backend/Types/Types';
15 18
 export class HeaderComponent implements OnInit, OnDestroy {
16 19
 
17 20
   private destroy$: Subject<void> = new Subject<void>();
18
-  userPictureOnly: boolean = false;
19 21
   user: User;
22
+  title = "Loading ... ";
20 23
 
21 24
   themes = [
22 25
     {
@@ -38,37 +41,46 @@ export class HeaderComponent implements OnInit, OnDestroy {
38 41
   ];
39 42
 
40 43
   currentTheme = 'dark';
41
-
42 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 52
   constructor(private sidebarService: NbSidebarService,
45
-              private menuService: NbMenuService,
53
+              private router: Router,
46 54
               private themeService: NbThemeService,
47 55
               private layoutService: LayoutService,
48 56
               private breakpointService: NbMediaBreakpointsService,
49
-              private loginService: LoginApiService,
57
+              private api: ApiService,
58
+              private dialogService : NbDialogService
50 59
              ) {}
51 60
 
52 61
   ngOnInit() {
53 62
     this.currentTheme = this.themeService.currentTheme;
54 63
 
55
-    this.user = this.loginService.getCurrentUser()
64
+    this.user = this.api.getCurrentUser()
56 65
     if(this.user)
57 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 85
     this.themeService.onThemeChange()
74 86
       .pipe(
@@ -78,6 +90,20 @@ export class HeaderComponent implements OnInit, OnDestroy {
78 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 107
   ngOnDestroy() {
82 108
     this.destroy$.next();
83 109
     this.destroy$.complete();
@@ -95,11 +121,11 @@ export class HeaderComponent implements OnInit, OnDestroy {
95 121
   }
96 122
 
97 123
   logout() {
98
-    this.loginService.logout()
124
+    this.api.logout()
99 125
   }
100 126
 
101 127
   navigateHome() {
102
-    this.menuService.navigateHome();
128
+    this.router.navigateByUrl('/')
103 129
     return false;
104 130
   }
105 131
 }

+ 12
- 1
src/frontend/src/app/@theme/theme.module.ts 查看文件

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

+ 2
- 2
src/frontend/src/app/app.component.ts 查看文件

@@ -5,7 +5,7 @@
5 5
  */
6 6
 import { Component, OnInit } from '@angular/core';
7 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 10
 @Component({
11 11
   selector: 'ngx-app',
@@ -13,7 +13,7 @@ import { LoginApiService } from './frontcraft/services/login-api';
13 13
 })
14 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 查看文件

@@ -5,7 +5,7 @@
5 5
  */
6 6
 import { BrowserModule } from '@angular/platform-browser';
7 7
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
8
-import { NgModule } from '@angular/core';
8
+import { NgModule, APP_INITIALIZER } from '@angular/core';
9 9
 import { HttpClientModule } from '@angular/common/http';
10 10
 import { CoreModule } from './@core/core.module';
11 11
 import { ThemeModule } from './@theme/theme.module';
@@ -20,6 +20,8 @@ import {
20 20
   NbToastrModule,
21 21
   NbWindowModule,
22 22
 } from '@nebular/theme';
23
+import { ApiService, initializeLoginSvc } from './frontcraft/services/login-api';
24
+import { CookieService } from 'ngx-cookie-service';
23 25
 
24 26
 @NgModule({
25 27
   declarations: [AppComponent],
@@ -42,7 +44,13 @@ import {
42 44
   ],
43 45
   bootstrap: [AppComponent],
44 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 56
 export class AppModule {

+ 2
- 2
src/frontend/src/app/frontcraft/auth/auth-layout.component.ts 查看文件

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

+ 1
- 1
src/frontend/src/app/frontcraft/auth/login/login.component.html 查看文件

@@ -3,7 +3,7 @@
3 3
 <form (ngSubmit)="login()" #form="ngForm" aria-labelledby="title">
4 4
 
5 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 7
     <input nbInput
8 8
            fullWidth
9 9
            [(ngModel)]="user.name"

+ 2
- 2
src/frontend/src/app/frontcraft/auth/login/login.component.ts 查看文件

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

+ 3
- 3
src/frontend/src/app/frontcraft/auth/logout/logout.component.ts 查看文件

@@ -1,5 +1,5 @@
1 1
 import { Component, OnInit } from '@angular/core';
2
-import { LoginApiService } from '../../services/login-api';
2
+import { ApiService } from '../../services/login-api';
3 3
 import { Router } from '@angular/router';
4 4
 
5 5
 @Component({
@@ -9,11 +9,11 @@ import { Router } from '@angular/router';
9 9
 export class LogoutComponent implements OnInit{
10 10
 
11 11
   constructor(
12
-    private loginApi : LoginApiService
12
+    private api : ApiService
13 13
   ){}
14 14
 
15 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 查看文件

@@ -2,21 +2,6 @@
2 2
 
3 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 5
   <div class="form-control-group">
21 6
     <label class="label" for="input-username">Username:</label>
22 7
     <input nbInput

+ 15
- 15
src/frontend/src/app/frontcraft/auth/register/register.component.ts 查看文件

@@ -1,8 +1,9 @@
1 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 3
 import { Router } from '@angular/router';
4 4
 import { _Rank, _Class, Class, User  } from '../../../../../../backend/Types/Types'
5 5
 import { specs  } from '../../../../../../backend/Types/PlayerSpecs'
6
+import { race } from 'rxjs';
6 7
 
7 8
 
8 9
 @Component({
@@ -20,7 +21,6 @@ export class RegisterComponent implements OnInit{
20 21
     spec : "Arms"
21 22
   }
22 23
 
23
-
24 24
   ranks = _Rank
25 25
   classes = _Class
26 26
   selectedClass : Class = "Warrior"
@@ -30,11 +30,11 @@ export class RegisterComponent implements OnInit{
30 30
 
31 31
   constructor(
32 32
     private router : Router,  
33
-    private loginApi : LoginApiService
33
+    private api : ApiService
34 34
   ){}
35 35
 
36 36
   ngOnInit(){
37
-    this.loginApi.checkLogin().then(loggedin => {
37
+    this.api.checkLogin().then(loggedin => {
38 38
       if(loggedin){
39 39
         this.router.navigateByUrl("/")
40 40
       }
@@ -60,27 +60,27 @@ export class RegisterComponent implements OnInit{
60 60
     this.character = {}
61 61
 
62 62
     try{
63
-      const usr = await this.loginApi.getUnprivilegedSocket().Authenticator.createUser(user)
63
+      const usr = await this.api.get('UserManager').createUser(user)
64 64
     }catch(e){
65 65
       alert("Error creating user"+e)
66 66
       return
67 67
     }
68 68
 
69 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 81
     }catch(e){
81 82
       alert("Error creating character"+e)
82 83
       return
83 84
     }
84 85
   }
85
-
86 86
 }

+ 13
- 0
src/frontend/src/app/frontcraft/pages/character/character.component.html 查看文件

@@ -1,10 +1,23 @@
1 1
 <nb-card 
2
+class = "col-12 col-xl-9"
2 3
 status="control">
3 4
     <nb-card-header [ngStyle]="{'color': color}" style="text-transform: capitalize;">
4 5
         {{char.charactername}}
5 6
     </nb-card-header>
6 7
     <nb-card-body>
8
+        {{char.race}}<br />
7 9
         {{char.specname}} {{char.class}}<br />
8 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 22
     </nb-card-body>
10 23
 </nb-card>

+ 7
- 5
src/frontend/src/app/frontcraft/pages/character/character.component.ts 查看文件

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

@@ -0,0 +1,34 @@
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 查看文件

@@ -0,0 +1,123 @@
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 查看文件

@@ -1,35 +0,0 @@
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 查看文件

@@ -1,42 +0,0 @@
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 查看文件

@@ -1,63 +0,0 @@
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 查看文件

@@ -1,10 +0,0 @@
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 查看文件

@@ -1,6 +1,6 @@
1
-import { Component, AfterContentChecked, OnInit } from '@angular/core';
1
+import { Component, AfterContentChecked, OnInit, AfterContentInit } from '@angular/core';
2 2
 import { NbMenuItem } from '@nebular/theme';
3
-import { LoginApiService } from '../services/login-api';
3
+import { ApiService } from '../services/login-api';
4 4
 import { Router } from '@angular/router';
5 5
 
6 6
 @Component({
@@ -9,35 +9,35 @@ import { Router } from '@angular/router';
9 9
 
10 10
     <ngx-one-column-layout>
11 11
     <nb-menu [items]="menu"></nb-menu>
12
-
13
-      <router-outlet></router-outlet>
12
+        <router-outlet ></router-outlet>
14 13
     </ngx-one-column-layout>
15 14
   `,
16 15
 })
17
-export class PagesLayoutComponent implements OnInit{
16
+export class PagesLayoutComponent implements AfterContentInit{
18 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 35
   constructor(
36
-    private loginSvc : LoginApiService,
36
+    private loginSvc : ApiService,
37 37
     private router : Router
38 38
   ){}
39 39
 
40
-  ngOnInit() : void {
40
+  ngAfterContentInit() : void {
41 41
     this.loginSvc.checkLogin().then(loggedin => {
42 42
       if(!loggedin){
43 43
         this.router.navigateByUrl("/auth")

+ 29
- 9
src/frontend/src/app/frontcraft/pages/pages-routing.module.ts 查看文件

@@ -1,35 +1,55 @@
1 1
 import { NgModule } from '@angular/core';
2 2
 import { RouterModule, Routes } from '@angular/router';
3
-import { FrontcraftDashboardComponent } from './dashboard/dashboard.component';
4 3
 import { PagesLayoutComponent } from './pages-layout.component';
5 4
 import { FrontcraftCharacterComponent } from './character/character.component';
6 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 13
 export const routes: Routes = [
10 14
     {
11 15
         path: '',
12 16
         component: PagesLayoutComponent,
13 17
         children: [
14
-            {
15
-                path: 'people',
16
-                component: FrontcraftPeopleComponent
17
-            },
18 18
             {
19 19
                 path: 'user/:name',
20 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 35
                 path: 'character/:name',
24 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 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 查看文件

@@ -2,7 +2,6 @@ import { CommonModule } from '@angular/common';
2 2
 import { NgModule } from '@angular/core';
3 3
 import { FormsModule } from '@angular/forms';
4 4
 import { RouterModule } from '@angular/router';
5
-
6 5
 import { 
7 6
   NbAlertModule,
8 7
   NbButtonModule,
@@ -10,10 +9,16 @@ import {
10 9
   NbInputModule,
11 10
   NbMenuModule,
12 11
   NbCardModule,
13
-  NbTreeGridModule
12
+  NbTreeGridModule,
13
+  NbListModule,
14
+  NbTabsetModule,
15
+  NbIconModule,
16
+  NbWindowModule,
17
+  NbPopoverModule,
18
+  NbDatepickerModule,
19
+  NbSelectModule
14 20
 } from '@nebular/theme';
15 21
 import { MyAuthRoutingModule } from './pages-routing.module';
16
-import { FrontcraftDashboardComponent } from './dashboard/dashboard.component';
17 22
 import { PagesLayoutComponent } from './pages-layout.component';
18 23
 import { ThemeModule } from '../../@theme/theme.module';
19 24
 import { DashboardModule } from '../../demo_pages/dashboard/dashboard.module';
@@ -21,7 +26,17 @@ import { ECommerceModule } from '../../demo_pages/e-commerce/e-commerce.module';
21 26
 import { MiscellaneousModule } from '../../demo_pages/miscellaneous/miscellaneous.module';
22 27
 import { FrontcraftCharacterComponent } from './character/character.component';
23 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 42
 @NgModule({
@@ -35,21 +50,43 @@ import { FrontcraftPeopleComponent } from './people/people.component';
35 50
     NbInputModule,
36 51
     NbButtonModule,
37 52
     NbCheckboxModule,
53
+    NbTabsetModule,
38 54
     ThemeModule,
39 55
     NbMenuModule,
40 56
     DashboardModule,
41 57
     ECommerceModule,
42 58
     MiscellaneousModule,
43 59
     NbCardModule,
44
-    
60
+    NbListModule,
61
+    NbIconModule,
62
+    NbEvaIconsModule,
63
+    NbPopoverModule,
64
+    NbDatepickerModule,
65
+    NbSelectModule,
66
+    NgxEchartsModule,
67
+    NbWindowModule.forChild(),
45 68
   ],
46 69
   declarations: [
47
-    FrontcraftPeopleComponent,
70
+    FrontcraftItemSelectComponent,
71
+    FrontcraftRaidComponent,
72
+    FrontcraftRaidsComponent,
48 73
     FrontcraftUserComponent,
49 74
     FrontcraftCharacterComponent,
50 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 91
 export class FrontcraftPagesModule {
55 92
 }

+ 0
- 36
src/frontend/src/app/frontcraft/pages/people/people.component.html 查看文件

@@ -1,36 +0,0 @@
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 查看文件

@@ -1,42 +0,0 @@
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 查看文件

@@ -1,64 +0,0 @@
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 查看文件

@@ -1,10 +0,0 @@
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 查看文件

@@ -0,0 +1,64 @@
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 查看文件

@@ -0,0 +1,30 @@
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 查看文件

@@ -0,0 +1,44 @@
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 查看文件

@@ -0,0 +1,119 @@
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 查看文件

@@ -0,0 +1,123 @@
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 查看文件

@@ -0,0 +1,28 @@
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 查看文件

@@ -0,0 +1,59 @@
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 查看文件

@@ -0,0 +1,37 @@
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 查看文件

@@ -0,0 +1,7 @@
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 查看文件

@@ -0,0 +1,54 @@
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 查看文件

@@ -0,0 +1,31 @@
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 查看文件

@@ -0,0 +1,31 @@
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 查看文件

@@ -0,0 +1,26 @@
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 查看文件

@@ -0,0 +1,45 @@
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 查看文件

@@ -0,0 +1,13 @@
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 查看文件

@@ -0,0 +1,135 @@
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 查看文件

@@ -1,11 +1,12 @@
1 1
 
2 2
 
3
-<nb-card
3
+<nb-card class="col-xl-9"
4 4
 accent="info">
5 5
     <nb-card-header>
6 6
         <h2 style="text-transform: capitalize;">{{user.username}}</h2>
7 7
     </nb-card-header>
8 8
     <nb-card-body>
9
+        {{user.currency}}
9 10
       <nb-card
10 11
       accent="control"
11 12
       *ngFor="let char of characters">
@@ -17,7 +18,20 @@ accent="info">
17 18
               </h4>
18 19
           </nb-card-header>
19 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 35
           </nb-card-body>
22 36
       </nb-card>
23 37
     </nb-card-body>

+ 19
- 20
src/frontend/src/app/frontcraft/pages/user/user.component.ts 查看文件

@@ -1,6 +1,6 @@
1 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 4
 import { User, Spec, Character } from '../../../../../../backend/Types/Types';
5 5
 import { getClassColor } from '../../../../../../backend/Types/PlayerSpecs';
6 6
 
@@ -14,25 +14,24 @@ export class FrontcraftUserComponent implements OnInit{
14 14
   characters : (Character & Spec)[] = []
15 15
 
16 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 查看文件

@@ -1,12 +1,13 @@
1 1
 import { Injectable, Injector, NgZone } from "@angular/core";
2 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 5
 import { CookieService } from 'ngx-cookie-service';
6 6
 import { Router } from '@angular/router';
7
+import { ShoutMessage } from '../../../../../backend/Components/Shoutbox/Interface';
7 8
 
8 9
 @Injectable()
9
-export class LoginApiService{
10
+export class ApiService{
10 11
     private socket:RPCSocket & FrontcraftIfc;
11 12
     private auth:Auth
12 13
     private privSocket: RPCSocket & SomeOf<FrontcraftFeatureIfc>
@@ -37,7 +38,7 @@ export class LoginApiService{
37 38
             sock.hook('getUserData', () => auth)
38 39
             sock.hook('navigate', (where:string) => {
39 40
                 this.ngZone.run( () => {
40
-                    this.injector.get(Router).navigateByUrl('/')
41
+                    this.injector.get(Router).navigateByUrl(where)
41 42
                 })
42 43
             })
43 44
             sock.on('error', (e) => {
@@ -55,13 +56,17 @@ export class LoginApiService{
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 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 67
         }catch(e){
63 68
             return
64
-        }
69
+        } 
65 70
     }
66 71
 
67 72
     getCurrentUser = () : User | undefined => {
@@ -70,7 +75,7 @@ export class LoginApiService{
70 75
 
71 76
     login = async (username: string, password: string) : Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>> => {
72 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 80
         if(!auth){ 
76 81
             await this.logout()
@@ -80,9 +85,14 @@ export class LoginApiService{
80 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 93
     logout = async () => {
84 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 96
         if(this.privSocket) this.privSocket.destroy()
87 97
         this.privSocket = null
88 98
         this.auth  = null
@@ -91,15 +101,16 @@ export class LoginApiService{
91 101
         })
92 102
     }
93 103
 
94
-
95 104
     initialize = async () : Promise<any> => {
96 105
         const sock = await RPCSocket.makeSocket<FrontcraftIfc>(20000, window.location.hostname)
106
+
97 107
         this.socket = sock
98 108
         try{
99 109
             const cookie = JSON.parse(this.cookieSvc.get('token'))
110
+            
100 111
             if(cookie != null) {
101 112
                 try{
102
-                    const auth = await sock.Authenticator.getAuth(cookie.token.value)
113
+                    const auth = await sock.UserManager.getAuth(cookie.token.value)
103 114
                     if(!auth) return sock
104 115
                     return await this.getPrivilegedSocket(auth)
105 116
                 }catch(e){
@@ -115,7 +126,7 @@ export class LoginApiService{
115 126
 
116 127
     async checkLogin() : Promise<boolean>{
117 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 130
         if(valid) return true
120 131
         await this.logout()
121 132
         return false
@@ -126,7 +137,7 @@ function str2arraybuf(str:string): ArrayBuffer {
126 137
     return new Buffer(str)
127 138
   }
128 139
   
129
-function buf2hex(buffer) { // buffer is an ArrayBuffer
140
+function buf2hex(buffer: ArrayBuffer) {
130 141
     return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
131 142
 }
132 143
 
@@ -137,6 +148,6 @@ export async function hash(value:string) : Promise<string>{
137 148
 }
138 149
 
139 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 查看文件

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

正在加载...
取消
保存