1.0.0 #253
| @@ -26,7 +26,7 @@ | ||||
|       "PyJWT==2.6.0", | ||||
|       "waitress==2.1.2", | ||||
|       "Flask-SocketIO==5.3.2", | ||||
|       "eventlet==0.33.2", | ||||
|       "eventlet==0.33.3", | ||||
|       "requests-oauthlib==1.3.1", | ||||
|       "icmplib==3.0.3", | ||||
|       "ariadne==0.17.1" | ||||
|   | ||||
| @@ -13,7 +13,6 @@ from bot_api.api import Api | ||||
| from bot_api.api_thread import ApiThread | ||||
| from bot_api.controller.auth_controller import AuthController | ||||
| from bot_api.controller.auth_discord_controller import AuthDiscordController | ||||
| from bot_api.controller.discord.server_controller import ServerController | ||||
| from bot_api.controller.grahpql_controller import GraphQLController | ||||
| from bot_api.controller.gui_controller import GuiController | ||||
| from bot_api.event.bot_api_on_ready_event import BotApiOnReadyEvent | ||||
| @@ -46,7 +45,6 @@ class ApiModule(ModuleABC): | ||||
|         services.add_transient(AuthDiscordController) | ||||
|         services.add_transient(GuiController) | ||||
|         services.add_transient(DiscordService) | ||||
|         services.add_transient(ServerController) | ||||
|         services.add_transient(GraphQLController) | ||||
|  | ||||
|         # cpl-discord | ||||
|   | ||||
| @@ -2,12 +2,9 @@ from cpl_core.application import ApplicationExtensionABC | ||||
| from cpl_core.configuration import ConfigurationABC | ||||
| from cpl_core.dependency_injection import ServiceProviderABC | ||||
|  | ||||
| from bot_api.abc.auth_service_abc import AuthServiceABC | ||||
| from bot_api.configuration.authentication_settings import AuthenticationSettings | ||||
| from bot_api.route.route import Route | ||||
| from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum | ||||
| from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings | ||||
| from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC | ||||
|  | ||||
|  | ||||
| class AppApiExtension(ApplicationExtensionABC): | ||||
| @@ -19,7 +16,4 @@ class AppApiExtension(ApplicationExtensionABC): | ||||
|         if not feature_flags.get_flag(FeatureFlagsEnum.api_module): | ||||
|             return | ||||
|  | ||||
|         auth_settings: AuthenticationSettings = config.get_configuration(AuthenticationSettings) | ||||
|         auth_users: AuthUserRepositoryABC = services.get_service(AuthUserRepositoryABC) | ||||
|         auth: AuthServiceABC = services.get_service(AuthServiceABC) | ||||
|         Route.init_authorize(auth_users, auth) | ||||
|         Route.init_authorize() | ||||
|   | ||||
 Submodule kdb-bot/src/bot_api/config updated: e6046881b5...27289afd5e
									
								
							| @@ -8,7 +8,7 @@ from cpl_core.utils import CredentialManager | ||||
| from cpl_discord.service import DiscordBotServiceABC | ||||
| from cpl_translation import TranslatePipe | ||||
| from flask import jsonify, Response | ||||
| from flask import request, session | ||||
| from flask import request | ||||
| from requests_oauthlib import OAuth2Session | ||||
|  | ||||
| from bot_api.abc.auth_service_abc import AuthServiceABC | ||||
| @@ -16,10 +16,8 @@ from bot_api.api import Api | ||||
| from bot_api.configuration.discord_authentication_settings import ( | ||||
|     DiscordAuthenticationSettings, | ||||
| ) | ||||
| from bot_api.json_processor import JSONProcessor | ||||
| from bot_api.logging.api_logger import ApiLogger | ||||
| from bot_api.model.auth_user_dto import AuthUserDTO | ||||
| from bot_api.model.o_auth_dto import OAuthDTO | ||||
| from bot_api.route.route import Route | ||||
| from bot_data.model.auth_role_enum import AuthRoleEnum | ||||
|  | ||||
| @@ -59,7 +57,7 @@ class AuthDiscordController: | ||||
|             self._bot.user.id, | ||||
|             redirect_uri=self._auth_settings.redirect_url, | ||||
|             state=request.args.get("state"), | ||||
|             scope=self._auth_settings.scope, | ||||
|             scope=self._auth_settings.scope.to_list(), | ||||
|         ) | ||||
|         token = discord.fetch_token( | ||||
|             self._auth_settings.token_url, | ||||
| @@ -74,7 +72,7 @@ class AuthDiscordController: | ||||
|         oauth = OAuth2Session( | ||||
|             self._bot.user.id, | ||||
|             redirect_uri=self._auth_settings.redirect_url, | ||||
|             scope=self._auth_settings.scope, | ||||
|             scope=self._auth_settings.scope.to_list(), | ||||
|         ) | ||||
|         login_url, state = oauth.authorization_url(self._auth_settings.auth_url) | ||||
|         return jsonify({"loginUrl": login_url}) | ||||
|   | ||||
| @@ -1,26 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
| bot Keksdose bot | ||||
| ~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
| Discord bot  for the Keksdose discord Server | ||||
|  | ||||
| :copyright: (c) 2022 - 2023 sh-edraft.de | ||||
| :license: MIT, see LICENSE for more details. | ||||
|  | ||||
| """ | ||||
|  | ||||
| __title__ = "bot_api.controller.discord" | ||||
| __author__ = "Sven Heidemann" | ||||
| __license__ = "MIT" | ||||
| __copyright__ = "Copyright (c) 2022 - 2023 sh-edraft.de" | ||||
| __version__ = "0.3.1" | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple("VersionInfo", "major minor micro") | ||||
| version_info = VersionInfo(major="0", minor="3", micro="1") | ||||
| @@ -1,67 +0,0 @@ | ||||
| from cpl_core.configuration import ConfigurationABC | ||||
| from cpl_core.environment import ApplicationEnvironmentABC | ||||
| from cpl_core.mailing import EMailClientABC, EMailClientSettings | ||||
| from cpl_translation import TranslatePipe | ||||
| from flask import Response, jsonify, request | ||||
|  | ||||
| from bot_api.api import Api | ||||
| from bot_api.filter.discord.server_select_criteria import ServerSelectCriteria | ||||
| from bot_api.json_processor import JSONProcessor | ||||
| from bot_api.logging.api_logger import ApiLogger | ||||
| from bot_api.route.route import Route | ||||
| from bot_api.service.discord_service import DiscordService | ||||
| from bot_data.model.auth_role_enum import AuthRoleEnum | ||||
|  | ||||
|  | ||||
| class ServerController: | ||||
|     BasePath = f"/api/discord/server" | ||||
|  | ||||
|     def __init__( | ||||
|         self, | ||||
|         config: ConfigurationABC, | ||||
|         env: ApplicationEnvironmentABC, | ||||
|         logger: ApiLogger, | ||||
|         t: TranslatePipe, | ||||
|         api: Api, | ||||
|         mail_settings: EMailClientSettings, | ||||
|         mailer: EMailClientABC, | ||||
|         discord_service: DiscordService, | ||||
|     ): | ||||
|         self._config = config | ||||
|         self._env = env | ||||
|         self._logger = logger | ||||
|         self._t = t | ||||
|         self._api = api | ||||
|         self._mail_settings = mail_settings | ||||
|         self._mailer = mailer | ||||
|         self._discord_service = discord_service | ||||
|  | ||||
|     @Route.get(f"{BasePath}/get/servers") | ||||
|     @Route.authorize(role=AuthRoleEnum.admin) | ||||
|     async def get_all_servers(self) -> Response: | ||||
|         result = await self._discord_service.get_all_servers() | ||||
|         result = result.select(lambda x: x.to_dict()).to_list() | ||||
|         return jsonify(result) | ||||
|  | ||||
|     @Route.get(f"{BasePath}/get/servers-by-user") | ||||
|     @Route.authorize | ||||
|     async def get_all_servers_by_user(self) -> Response: | ||||
|         result = await self._discord_service.get_all_servers_by_user() | ||||
|         result = result.select(lambda x: x.to_dict()).to_list() | ||||
|         return jsonify(result) | ||||
|  | ||||
|     @Route.post(f"{BasePath}/get/filtered") | ||||
|     @Route.authorize | ||||
|     async def get_filtered_servers(self) -> Response: | ||||
|         dto: ServerSelectCriteria = JSONProcessor.process( | ||||
|             ServerSelectCriteria, request.get_json(force=True, silent=True) | ||||
|         ) | ||||
|         result = await self._discord_service.get_filtered_servers_async(dto) | ||||
|         result.result = result.result.select(lambda x: x.to_dict()).to_list() | ||||
|         return jsonify(result.to_dict()) | ||||
|  | ||||
|     @Route.get(f"{BasePath}/get/<id>") | ||||
|     @Route.authorize | ||||
|     async def get_server_by_id(self, id: int) -> Response: | ||||
|         result = await self._discord_service.get_server_by_id_async(id).to_list() | ||||
|         return jsonify(result.to_dict()) | ||||
| @@ -25,10 +25,15 @@ class GraphQLController: | ||||
|         self._schema = schema | ||||
|  | ||||
|     @Route.get(f"{BasePath}/playground") | ||||
|     @Route.authorize(skip_in_dev=True) | ||||
|     async def playground(self): | ||||
|         if self._env.environment_name != "development": | ||||
|             return "", 403 | ||||
|  | ||||
|         return PLAYGROUND_HTML, 200 | ||||
|  | ||||
|     @Route.post(f"{BasePath}") | ||||
|     @Route.authorize | ||||
|     async def graphql(self): | ||||
|         data = request.get_json() | ||||
|  | ||||
|   | ||||
| @@ -2,6 +2,8 @@ import functools | ||||
| from functools import wraps | ||||
| from typing import Optional, Callable | ||||
|  | ||||
| from cpl_core.dependency_injection import ServiceProviderABC | ||||
| from cpl_core.environment import ApplicationEnvironmentABC | ||||
| from flask import request, jsonify | ||||
| from flask_cors import cross_origin | ||||
|  | ||||
| @@ -18,19 +20,25 @@ class Route: | ||||
|  | ||||
|     _auth_users: Optional[AuthUserRepositoryABC] = None | ||||
|     _auth: Optional[AuthServiceABC] = None | ||||
|     _env = "production" | ||||
|  | ||||
|     @classmethod | ||||
|     def init_authorize(cls, auth_users: AuthUserRepositoryABC, auth: AuthServiceABC): | ||||
|     @ServiceProviderABC.inject | ||||
|     def init_authorize(cls, env: ApplicationEnvironmentABC, auth_users: AuthUserRepositoryABC, auth: AuthServiceABC): | ||||
|         cls._auth_users = auth_users | ||||
|         cls._auth = auth | ||||
|         cls._env = env.environment_name | ||||
|  | ||||
|     @classmethod | ||||
|     def authorize(cls, f: Callable = None, role: AuthRoleEnum = None): | ||||
|     def authorize(cls, f: Callable = None, role: AuthRoleEnum = None, skip_in_dev=False): | ||||
|         if f is None: | ||||
|             return functools.partial(cls.authorize, role=role) | ||||
|             return functools.partial(cls.authorize, role=role, skip_in_dev=skip_in_dev) | ||||
|  | ||||
|         @wraps(f) | ||||
|         async def decorator(*args, **kwargs): | ||||
|             if skip_in_dev and cls._env == "development": | ||||
|                 return await f(*args, **kwargs) | ||||
|  | ||||
|             token = None | ||||
|             if "Authorization" in request.headers: | ||||
|                 bearer = request.headers.get("Authorization") | ||||
|   | ||||
| @@ -2,6 +2,7 @@ type Server implements TableQuery { | ||||
|     id: ID | ||||
|     discordId: String | ||||
|     name: String | ||||
|     iconURL: String | ||||
|  | ||||
|     autoRoleCount: Int | ||||
|     autoRoles(filter: AutoRoleFilter, page: Page, sort: Sort): [AutoRole] | ||||
|   | ||||
| @@ -35,6 +35,7 @@ class ServerQuery(DataQueryABC): | ||||
|         self.set_field("id", self.resolve_id) | ||||
|         self.set_field("discordId", self.resolve_discord_id) | ||||
|         self.set_field("name", self.resolve_name) | ||||
|         self.set_field("iconURL", self.resolve_icon_url) | ||||
|  | ||||
|         self.add_collection( | ||||
|             "autoRole", lambda server, *_: self._auto_roles.get_auto_roles_by_server_id(server.server_id) | ||||
| @@ -54,3 +55,7 @@ class ServerQuery(DataQueryABC): | ||||
|     def resolve_name(self, server: Server, *_): | ||||
|         guild = self._bot.get_guild(server.discord_server_id) | ||||
|         return None if guild is None else guild.name | ||||
|  | ||||
|     def resolve_icon_url(self, server: Server, *_): | ||||
|         guild = self._bot.get_guild(server.discord_server_id) | ||||
|         return None if guild is None else guild.icon.url | ||||
|   | ||||
							
								
								
									
										41199
									
								
								kdb-web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										41199
									
								
								kdb-web/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "kdb-web", | ||||
|     "version": "0.3.0", | ||||
|     "version": "0.3.dev162-2", | ||||
|     "scripts": { | ||||
|         "ng": "ng", | ||||
|         "update-version": "ts-node-esm update-version.ts", | ||||
| @@ -16,29 +16,29 @@ | ||||
|     }, | ||||
|     "private": true, | ||||
|     "dependencies": { | ||||
|         "@angular/animations": "^14.0.0", | ||||
|         "@angular/common": "^14.0.0", | ||||
|         "@angular/compiler": "^14.0.0", | ||||
|         "@angular/core": "^14.0.0", | ||||
|         "@angular/forms": "^14.0.0", | ||||
|         "@angular/platform-browser": "^14.0.0", | ||||
|         "@angular/platform-browser-dynamic": "^14.0.0", | ||||
|         "@angular/router": "^14.0.0", | ||||
|         "@angular/animations": "^15.1.4", | ||||
|         "@angular/common": "^15.1.4", | ||||
|         "@angular/compiler": "^15.1.4", | ||||
|         "@angular/core": "^15.1.4", | ||||
|         "@angular/forms": "^15.1.4", | ||||
|         "@angular/platform-browser": "^15.1.4", | ||||
|         "@angular/platform-browser-dynamic": "^15.1.4", | ||||
|         "@angular/router": "^15.1.4", | ||||
|         "@auth0/angular-jwt": "^5.1.0", | ||||
|         "@microsoft/signalr": "^6.0.9", | ||||
|         "@ngx-translate/core": "^14.0.0", | ||||
|         "@ngx-translate/http-loader": "^7.0.0", | ||||
|         "@types/socket.io-client": "^3.0.0", | ||||
|         "primeicons": "^6.0.1", | ||||
|         "primeng": "^14.1.2", | ||||
|         "primeng": "^15.2.0", | ||||
|         "rxjs": "~7.5.0", | ||||
|         "socket.io-client": "^4.5.3", | ||||
|         "zone.js": "~0.11.4" | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|         "@angular-devkit/build-angular": "^14.0.0", | ||||
|         "@angular/cli": "~14.0.0", | ||||
|         "@angular/compiler-cli": "^14.0.0", | ||||
|         "@angular-devkit/build-angular": "^15.1.5", | ||||
|         "@angular/cli": "~15.1.5", | ||||
|         "@angular/compiler-cli": "^15.1.4", | ||||
|         "@types/jasmine": "~4.0.0", | ||||
|         "@types/node": "^18.11.9", | ||||
|         "jasmine-core": "~4.1.0", | ||||
| @@ -48,6 +48,6 @@ | ||||
|         "karma-jasmine": "~5.0.0", | ||||
|         "karma-jasmine-html-reporter": "~1.7.0", | ||||
|         "tslib": "^2.4.1", | ||||
|         "typescript": "~4.7.2" | ||||
|         "typescript": "~4.9.5" | ||||
|     } | ||||
| } | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { HttpClient } from '@angular/common/http'; | ||||
| import { HttpClient, HttpClientModule } from '@angular/common/http'; | ||||
| import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core'; | ||||
| import { BrowserModule } from '@angular/platform-browser'; | ||||
| import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | ||||
| @@ -47,7 +47,8 @@ import { SettingsService } from './services/settings/settings.service'; | ||||
|         useFactory: HttpLoaderFactory, | ||||
|         deps: [HttpClient] | ||||
|       } | ||||
|     }) | ||||
|     }), | ||||
|     HttpClientModule | ||||
|   ], | ||||
|   providers: [ | ||||
|     { | ||||
|   | ||||
| @@ -17,7 +17,7 @@ export class SidebarComponent implements OnInit { | ||||
|   isSidebarOpen: boolean = true; | ||||
|   menuItems!: MenuItem[]; | ||||
|  | ||||
|   private serverId!: number; | ||||
|   private serverId?: number; | ||||
|  | ||||
|   constructor( | ||||
|     private authService: AuthService, | ||||
| @@ -40,7 +40,7 @@ export class SidebarComponent implements OnInit { | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       this.serverId = server.serverId; | ||||
|       this.serverId = server.id; | ||||
|       this.setMenu(); | ||||
|     }); | ||||
|   } | ||||
|   | ||||
							
								
								
									
										4
									
								
								kdb-web/src/app/models/data/data.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								kdb-web/src/app/models/data/data.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| export interface Data { | ||||
|   createdAt: string; | ||||
|   modifiedAt: string; | ||||
| } | ||||
							
								
								
									
										16
									
								
								kdb-web/src/app/models/data/server.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								kdb-web/src/app/models/data/server.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| import { Data } from "./data.model"; | ||||
|  | ||||
| export interface Server extends Data { | ||||
|   id?: number; | ||||
|   discordId?: number; | ||||
|   name?: string; | ||||
|   iconURL?: string; | ||||
|   autoRoleCount?: number; | ||||
|   autoRoles?: []; | ||||
|   clientCount?: number; | ||||
|   clients?: []; | ||||
|   levelCount?: number; | ||||
|   levels?: []; | ||||
|   userCount?: number; | ||||
|   users?: []; | ||||
| } | ||||
| @@ -1,7 +0,0 @@ | ||||
| export interface ServerDTO { | ||||
|     serverId: number; | ||||
|     discordId: number; | ||||
|     name: string; | ||||
|     memberCount: number; | ||||
|     iconURL: string | null; | ||||
| } | ||||
							
								
								
									
										4
									
								
								kdb-web/src/app/models/graphql/filter/page.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								kdb-web/src/app/models/graphql/filter/page.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| export interface Page { | ||||
|   pageIndex?: number; | ||||
|   pageSize?: number; | ||||
| } | ||||
							
								
								
									
										4
									
								
								kdb-web/src/app/models/graphql/filter/sort.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								kdb-web/src/app/models/graphql/filter/sort.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| export interface Sort { | ||||
|   sortColumn?: string; | ||||
|   sortDirection?: string; | ||||
| } | ||||
							
								
								
									
										13
									
								
								kdb-web/src/app/models/graphql/queries.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								kdb-web/src/app/models/graphql/queries.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| export class Queries { | ||||
|   static serverInfoQuery = ` | ||||
|     query ServerInfo($filter: ServerFilter, $page: Page, $sort: Sort) { | ||||
|       serverCount | ||||
|       servers(filter: $filter, page: $page, sort: $sort) { | ||||
|         id | ||||
|         name | ||||
|         iconURL | ||||
|         userCount | ||||
|       } | ||||
|     } | ||||
|   `; | ||||
| } | ||||
							
								
								
									
										6
									
								
								kdb-web/src/app/models/graphql/query.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								kdb-web/src/app/models/graphql/query.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| import { Server } from "../data/server.model"; | ||||
|  | ||||
| export interface Query { | ||||
|   serverCount: number; | ||||
|   servers: Server[]; | ||||
| } | ||||
							
								
								
									
										5
									
								
								kdb-web/src/app/models/graphql/result.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								kdb-web/src/app/models/graphql/result.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| import { Query } from "./query.model"; | ||||
|  | ||||
| export interface QueryResult { | ||||
|   data: any; | ||||
| } | ||||
							
								
								
									
										8
									
								
								kdb-web/src/app/models/graphql/variables.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								kdb-web/src/app/models/graphql/variables.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| import { Page } from "./filter/page.model"; | ||||
| import { Sort } from "./filter/sort.model"; | ||||
|  | ||||
| export interface Variables { | ||||
|   filter?: object; | ||||
|   page?: Page; | ||||
|   sort?: Sort; | ||||
| } | ||||
| @@ -1,7 +0,0 @@ | ||||
| import { AuthUserDTO } from "../../auth/auth-user.dto"; | ||||
| import { ServerDTO } from "../../discord/server.dto"; | ||||
|  | ||||
| export interface GetFilteredServersResultDTO { | ||||
|     servers: ServerDTO[]; | ||||
|     totalCount: number; | ||||
| } | ||||
| @@ -1,5 +0,0 @@ | ||||
| import { SelectCriterion } from "../select-criterion.model"; | ||||
|  | ||||
| export interface ServerSelectCriterion extends SelectCriterion { | ||||
|     name: string | null; | ||||
| } | ||||
| @@ -38,7 +38,7 @@ | ||||
|  | ||||
|                         <div class="data"> | ||||
|                             <i class="pi pi-users"></i> | ||||
|                             {{server.memberCount}} | ||||
|                             {{server.userCount}} | ||||
|                             {{'view.dashboard.server.member_count' | translate}} | ||||
|                         </div> | ||||
|                     </div> | ||||
| @@ -47,4 +47,4 @@ | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| </div> | ||||
|   | ||||
| @@ -1,35 +1,42 @@ | ||||
| import { Component, OnInit } from '@angular/core'; | ||||
| import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; | ||||
| import { Router } from '@angular/router'; | ||||
| import { TranslateService } from '@ngx-translate/core'; | ||||
| import { LazyLoadEvent } from 'primeng/api'; | ||||
| import { catchError, debounceTime, throwError } from 'rxjs'; | ||||
| import { ServerDTO } from 'src/app/models/discord/server.dto'; | ||||
| import { ServerSelectCriterion } from 'src/app/models/selection/server/server-select-criterion.dto'; | ||||
| import { ConfirmationDialogService } from 'src/app/services/confirmation-dialog/confirmation-dialog.service'; | ||||
| import { DataService } from 'src/app/services/data/data.service'; | ||||
| import { ServerService } from 'src/app/services/data/server.service'; | ||||
| import { SpinnerService } from 'src/app/services/spinner/spinner.service'; | ||||
| import { ToastService } from 'src/app/services/toast/toast.service'; | ||||
| import { Component, OnInit } from "@angular/core"; | ||||
| import { FormBuilder, FormControl, FormGroup } from "@angular/forms"; | ||||
| import { Router } from "@angular/router"; | ||||
| import { TranslateService } from "@ngx-translate/core"; | ||||
| import { LazyLoadEvent } from "primeng/api"; | ||||
| import { debounceTime, throwError } from "rxjs"; | ||||
| import { ConfirmationDialogService } from "src/app/services/confirmation-dialog/confirmation-dialog.service"; | ||||
| import { DataService } from "src/app/services/data/data.service"; | ||||
| import { ServerService } from "src/app/services/data/server.service"; | ||||
| import { SpinnerService } from "src/app/services/spinner/spinner.service"; | ||||
| import { ToastService } from "src/app/services/toast/toast.service"; | ||||
| import { Server } from "../../../../../models/data/server.model"; | ||||
| import { catchError } from "rxjs/operators"; | ||||
| import { Queries } from "../../../../../models/graphql/queries.model"; | ||||
| import { Page } from "../../../../../models/graphql/filter/page.model"; | ||||
| import { Sort } from "../../../../../models/graphql/filter/sort.model"; | ||||
| import { Query } from "../../../../../models/graphql/query.model"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-dashboard', | ||||
|   templateUrl: './dashboard.component.html', | ||||
|   styleUrls: ['./dashboard.component.scss'] | ||||
|   selector: "app-dashboard", | ||||
|   templateUrl: "./dashboard.component.html", | ||||
|   styleUrls: ["./dashboard.component.scss"] | ||||
| }) | ||||
| export class DashboardComponent implements OnInit { | ||||
|  | ||||
|   servers: ServerDTO[] = []; | ||||
|   servers: Server[] = []; | ||||
|  | ||||
|   searchCriterions: ServerSelectCriterion = { | ||||
|     name: null, | ||||
|     pageIndex: 0, | ||||
|     pageSize: 10, | ||||
|     sortColumn: null, | ||||
|     sortDirection: null | ||||
|   }; | ||||
|   totalRecords!: number; | ||||
|  | ||||
|   filter = { | ||||
|     name: "" | ||||
|   }; | ||||
|  | ||||
|   page: Page = { | ||||
|     pageIndex: 0, | ||||
|     pageSize: 10 | ||||
|   }; | ||||
|  | ||||
|   sort!: Sort; | ||||
|  | ||||
|   filterForm!: FormGroup<{ | ||||
|     name: FormControl<string | null>, | ||||
| @@ -44,60 +51,67 @@ export class DashboardComponent implements OnInit { | ||||
|     private translate: TranslateService, | ||||
|     private router: Router, | ||||
|     private serverService: ServerService | ||||
|   ) { } | ||||
|   ) { | ||||
|   } | ||||
|  | ||||
|   ngOnInit(): void { | ||||
|   async ngOnInit(): Promise<void> { | ||||
|     this.spinnerService.showSpinner(); | ||||
|  | ||||
|     this.setFilterForm(); | ||||
|     this.loadNextPage(); | ||||
|   } | ||||
|  | ||||
|   setFilterForm() { | ||||
|     this.filterForm = this.fb.group({ | ||||
|       name: [''], | ||||
|       name: [""] | ||||
|     }); | ||||
|  | ||||
|     this.filterForm.valueChanges.pipe( | ||||
|       debounceTime(600) | ||||
|     ).subscribe(changes => { | ||||
|     ).subscribe(async changes => { | ||||
|       if (changes.name) { | ||||
|         this.searchCriterions.name = changes.name; | ||||
|       } else { | ||||
|         this.searchCriterions.name = null; | ||||
|         this.filter.name = changes.name; | ||||
|       } | ||||
|  | ||||
|       if (this.searchCriterions.pageSize) | ||||
|         this.searchCriterions.pageSize = 10; | ||||
|       if (this.page.pageSize) | ||||
|         this.page.pageSize = 10; | ||||
|  | ||||
|       if (this.searchCriterions.pageSize) | ||||
|         this.searchCriterions.pageIndex = 0; | ||||
|       if (this.page.pageSize) | ||||
|         this.page.pageIndex = 0; | ||||
|  | ||||
|       this.loadNextPage(); | ||||
|       await this.loadNextPage(); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   loadNextPage() { | ||||
|     this.spinnerService.showSpinner(); | ||||
|     this.data.getFilteredServers(this.searchCriterions).pipe(catchError(err => { | ||||
|     this.data.query<Query>(Queries.serverInfoQuery,{ | ||||
|       filter: this.filter, | ||||
|       page: this.page, | ||||
|       sort: this.sort, | ||||
|     }).pipe(catchError(err => { | ||||
|       this.spinnerService.hideSpinner(); | ||||
|       return throwError(() => err); | ||||
|     })).subscribe(list => { | ||||
|       this.totalRecords = list.totalCount; | ||||
|       this.servers = list.servers; | ||||
|     })).subscribe(data => { | ||||
|       this.totalRecords = data.serverCount; | ||||
|       this.servers = data.servers; | ||||
|       this.spinnerService.hideSpinner(); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   nextPage(event: LazyLoadEvent) { | ||||
|     this.searchCriterions.pageSize = event.rows ?? 0; | ||||
|     this.page.pageSize = event.rows ?? 0; | ||||
|     if (event.first != null && event.rows != null) | ||||
|       this.searchCriterions.pageIndex = event.first / event.rows; | ||||
|     this.searchCriterions.sortColumn = event.sortField ?? null; | ||||
|     this.searchCriterions.sortDirection = event.sortOrder === 1 ? 'asc' : event.sortOrder === -1 ? 'desc' : 'asc'; | ||||
|       this.page.pageIndex = event.first / event.rows; | ||||
|  | ||||
|     this.sort = { | ||||
|       sortColumn: event.sortField ?? "", | ||||
|       sortDirection: event.sortOrder === 1 ? "asc" : event.sortOrder === -1 ? "desc" : "asc" | ||||
|     }; | ||||
|  | ||||
|     if (event.filters) { | ||||
|       // + "" => convert to string | ||||
|       this.searchCriterions.name = event.filters['name'] ? event.filters['name'] + "" : null; | ||||
|       this.filter.name = event.filters["name"] ? event.filters["name"] + "" : ""; | ||||
|     } | ||||
|  | ||||
|     this.loadNextPage(); | ||||
| @@ -107,9 +121,9 @@ export class DashboardComponent implements OnInit { | ||||
|     this.filterForm.reset(); | ||||
|   } | ||||
|  | ||||
|   selectServer(server: ServerDTO) { | ||||
|   selectServer(server: Server) { | ||||
|     this.serverService.server$.next(server); | ||||
|     this.router.navigate(['/server']); | ||||
|     this.router.navigate(["/server"]); | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -24,7 +24,7 @@ | ||||
|  | ||||
|                         <div class="data"> | ||||
|                             <i class="pi pi-users"></i> | ||||
|                             {{server.memberCount}} | ||||
|                             {{server.userCount}} | ||||
|                             {{'view.dashboard.server.member_count' | translate}} | ||||
|                         </div> | ||||
|                     </div> | ||||
| @@ -33,4 +33,4 @@ | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| </div> | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { Component, OnInit } from '@angular/core'; | ||||
| import { ActivatedRoute, Router } from '@angular/router'; | ||||
| import { ServerDTO } from 'src/app/models/discord/server.dto'; | ||||
| import { DataService } from 'src/app/services/data/data.service'; | ||||
| import { ServerService } from 'src/app/services/data/server.service'; | ||||
| import { SpinnerService } from 'src/app/services/spinner/spinner.service'; | ||||
| import { Component, OnInit } from "@angular/core"; | ||||
| import { ActivatedRoute, Router } from "@angular/router"; | ||||
| import { Server } from "src/app/models/data/server.model"; | ||||
| import { DataService } from "src/app/services/data/data.service"; | ||||
| import { ServerService } from "src/app/services/data/server.service"; | ||||
| import { SpinnerService } from "src/app/services/spinner/spinner.service"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-server-dashboard', | ||||
| @@ -13,7 +13,7 @@ import { SpinnerService } from 'src/app/services/spinner/spinner.service'; | ||||
| export class ServerDashboardComponent implements OnInit { | ||||
|  | ||||
|   id!: number; | ||||
|   server!: ServerDTO; | ||||
|   server!: Server; | ||||
|  | ||||
|   constructor( | ||||
|     private route: ActivatedRoute, | ||||
|   | ||||
| @@ -217,7 +217,7 @@ export class AuthService { | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   getDecodedToken(): { [key: string]: any } { | ||||
|   getDecodedToken(): { [key: string]: any } | null{ | ||||
|     return this.jwtHelper.decodeToken(this.getToken().token); | ||||
|   } | ||||
|  | ||||
| @@ -288,10 +288,11 @@ export class AuthService { | ||||
|       return false; | ||||
|     } | ||||
|     const token = this.getDecodedToken(); | ||||
|     if (!token) return false; | ||||
|     return AuthRoles[token['role']] === AuthRoles[role]; | ||||
|   } | ||||
|  | ||||
|   getEMailFromDecodedToken(token: { [key: string]: any }): string | null { | ||||
|   getEMailFromDecodedToken(token: { [key: string]: any } | null): string | null { | ||||
|     if (!token) { | ||||
|       return null; | ||||
|     } | ||||
|   | ||||
| @@ -1,53 +1,39 @@ | ||||
| import { HttpClient, HttpHeaders } from '@angular/common/http'; | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { Observable } from 'rxjs'; | ||||
| import { ServerDTO } from 'src/app/models/discord/server.dto'; | ||||
| import { GetFilteredServersResultDTO } from 'src/app/models/selection/server/get-filtered-servers-result.dto'; | ||||
| import { ServerSelectCriterion } from 'src/app/models/selection/server/server-select-criterion.dto'; | ||||
| import { SettingsService } from '../settings/settings.service'; | ||||
| import { HttpClient } from "@angular/common/http"; | ||||
| import { Injectable } from "@angular/core"; | ||||
| import { SettingsService } from "../settings/settings.service"; | ||||
| import { map, Observable } from "rxjs"; | ||||
| import { Variables } from "../../models/graphql/variables.model"; | ||||
|  | ||||
| @Injectable({ | ||||
|   providedIn: 'root' | ||||
|   providedIn: "root" | ||||
| }) | ||||
| export class DataService { | ||||
|  | ||||
|   constructor( | ||||
|     private appsettings: SettingsService, | ||||
|     private http: HttpClient, | ||||
|   ) { } | ||||
|  | ||||
|  | ||||
|  | ||||
|   /* data requests */ | ||||
|   getAllServers(): Observable<Array<ServerDTO>> { | ||||
|     return this.http.get<Array<ServerDTO>>(`${this.appsettings.getApiURL()}/api/discord/server/servers`, { | ||||
|       headers: new HttpHeaders({ | ||||
|         'Content-Type': 'application/json' | ||||
|       }) | ||||
|     }); | ||||
|   } | ||||
|    | ||||
|   getAllServersByUser(): Observable<Array<ServerDTO>> { | ||||
|     return this.http.get<Array<ServerDTO>>(`${this.appsettings.getApiURL()}/api/discord/server/servers-by-user`, { | ||||
|       headers: new HttpHeaders({ | ||||
|         'Content-Type': 'application/json' | ||||
|       }) | ||||
|     }); | ||||
|     private http: HttpClient | ||||
|   ) { | ||||
|   } | ||||
|  | ||||
|   getFilteredServers(selectCriterions: ServerSelectCriterion): Observable<GetFilteredServersResultDTO> { | ||||
|     return this.http.post<GetFilteredServersResultDTO>(`${this.appsettings.getApiURL()}/api/discord/server/get/filtered`, selectCriterions, { | ||||
|       headers: new HttpHeaders({ | ||||
|         'Content-Type': 'application/json' | ||||
|   // query(query: string, variables: object = {}): Observable<QueryResult> { | ||||
|   //   return this.http.post<QueryResult>(`${this.appsettings.getApiURL()}/api/graphql`, | ||||
|   //     JSON.stringify({ | ||||
|   //       query, variables | ||||
|   //     }), { | ||||
|   //       headers: new HttpHeaders({ | ||||
|   //         "Content-Type": "application/json" | ||||
|   //       }) | ||||
|   //     } | ||||
|   //   ); | ||||
|   // } | ||||
|  | ||||
|   public query<T>(query: string, variables?: Variables): Observable<T> { | ||||
|     return this.http | ||||
|       .post<{ data: T }>(`${this.appsettings.getApiURL()}/api/graphql`, { | ||||
|         query: query, | ||||
|         variables: variables | ||||
|       }) | ||||
|     }); | ||||
|   } | ||||
|    | ||||
|   getServerByID(id: number): Observable<ServerDTO> { | ||||
|     return this.http.get<ServerDTO>(`${this.appsettings.getApiURL()}/api/discord/server/get/${id}`, { | ||||
|       headers: new HttpHeaders({ | ||||
|         'Content-Type': 'application/json' | ||||
|       }) | ||||
|     }); | ||||
|       .pipe(map((d) => d.data)); | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { BehaviorSubject, Subject } from 'rxjs'; | ||||
| import { ServerDTO } from 'src/app/models/discord/server.dto'; | ||||
| import { Injectable } from "@angular/core"; | ||||
| import { BehaviorSubject } from "rxjs"; | ||||
| import { Server } from "src/app/models/data/server.model"; | ||||
|  | ||||
| @Injectable({ | ||||
|   providedIn: 'root' | ||||
| }) | ||||
| export class ServerService { | ||||
|  | ||||
|   private server!: ServerDTO; | ||||
|   server$ = new BehaviorSubject<ServerDTO | null>(null); | ||||
|   private server!: Server; | ||||
|   server$ = new BehaviorSubject<Server | null>(null); | ||||
|  | ||||
|   constructor() { | ||||
|     this.server$.subscribe(server => { | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|     "WebVersion": { | ||||
|         "Major": "0", | ||||
|         "Minor": "3", | ||||
|         "Micro": "0" | ||||
|         "Micro": "dev162-2" | ||||
|     }, | ||||
|     "Themes": [ | ||||
|         { | ||||
|   | ||||
| @@ -7,20 +7,8 @@ import { | ||||
|   platformBrowserDynamicTesting | ||||
| } from '@angular/platform-browser-dynamic/testing'; | ||||
|  | ||||
| declare const require: { | ||||
|   context(path: string, deep?: boolean, filter?: RegExp): { | ||||
|     <T>(id: string): T; | ||||
|     keys(): string[]; | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| // First, initialize the Angular testing environment. | ||||
| getTestBed().initTestEnvironment( | ||||
|   BrowserDynamicTestingModule, | ||||
|   platformBrowserDynamicTesting(), | ||||
| ); | ||||
|  | ||||
| // Then we find all the tests. | ||||
| const context = require.context('./', true, /\.spec\.ts$/); | ||||
| // And load the modules. | ||||
| context.keys().forEach(context); | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| /* To learn more about this file see: https://angular.io/config/tsconfig. */ | ||||
| { | ||||
|   "ts-node": { | ||||
|     "compilerOptions": { | ||||
| @@ -21,12 +20,15 @@ | ||||
|     "experimentalDecorators": true, | ||||
|     "moduleResolution": "node", | ||||
|     "importHelpers": true, | ||||
|     "target": "es2020", | ||||
|     "target": "ES2022", | ||||
|     "module": "es2020", | ||||
|     "lib": [ | ||||
|       "es2020", | ||||
|       "dom" | ||||
|     ] | ||||
|       "dom", | ||||
|       "esnext.asynciterable" | ||||
|     ], | ||||
|     "allowSyntheticDefaultImports": true, | ||||
|     "useDefineForClassFields": false | ||||
|   }, | ||||
|   "angularCompilerOptions": { | ||||
|     "enableI18nLegacyMessageIdFormat": false, | ||||
| @@ -34,4 +36,4 @@ | ||||
|     "strictInputAccessModifiers": true, | ||||
|     "strictTemplates": true | ||||
|   } | ||||
| } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user