From 0c71e4934e63d8cdb5a98447255acfa585eceeb3 Mon Sep 17 00:00:00 2001 From: Nick Jungmann Date: Wed, 12 Oct 2022 20:42:40 +0200 Subject: [PATCH 001/275] Renamed user-info and added it to user group #61 --- src/bot/bot.json | 2 +- src/bot/config/appsettings.PC-Nick.json | 86 +++++++++++++++++++ .../{user_info_command.py => user_group.py} | 11 ++- src/modules/moderator/moderator_module.py | 4 +- 4 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 src/bot/config/appsettings.PC-Nick.json rename src/modules/moderator/command/{user_info_command.py => user_group.py} (94%) diff --git a/src/bot/bot.json b/src/bot/bot.json index 11f9a61e..8bbcdf43 100644 --- a/src/bot/bot.json +++ b/src/bot/bot.json @@ -17,7 +17,7 @@ "LicenseDescription": "MIT, see LICENSE for more details.", "Dependencies": [ "cpl-core==2022.10.0.post6", - "cpl-translation==2022.10.0", + "cpl-translation==2022.10.0.post1", "cpl-query==2022.10.0", "cpl-discord==2022.10.0.post5" ], diff --git a/src/bot/config/appsettings.PC-Nick.json b/src/bot/config/appsettings.PC-Nick.json new file mode 100644 index 00000000..d296c19c --- /dev/null +++ b/src/bot/config/appsettings.PC-Nick.json @@ -0,0 +1,86 @@ +{ + "TimeFormatSettings": { + "DateFormat": "%Y-%m-%d", + "TimeFormat": "%H:%M:%S", + "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", + "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" + }, + "LoggingSettings": { + "Path": "logs/", + "Filename": "bot.log", + "ConsoleLogLevel": "TRACE", + "FileLogLevel": "TRACE" + }, + "BotLoggingSettings": { + "Command": { + "Path": "logs/", + "Filename": "commands.log", + "ConsoleLogLevel": "DEBUG", + "FileLogLevel": "TRACE" + }, + "Database": { + "Path": "logs/", + "Filename": "database.log", + "ConsoleLogLevel": "DEBUG", + "FileLogLevel": "TRACE" + }, + "Message": { + "Path": "logs/", + "Filename": "message.log", + "ConsoleLogLevel": "DEBUG", + "FileLogLevel": "TRACE" + } + }, + "DatabaseSettings": { + "Host": "localhost", + "User": "root", + "Password": "MTAwNjE5OTdOaWNrLko=", + "Database": "kd_kdb", + "Charset": "utf8mb4", + "UseUnicode": "true", + "Buffered": "true", + "AuthPlugin": "mysql_native_password" + }, + "DiscordBot": { + "Token": "MTAyOTgxMjE0Mjk4NTE5MTYxNA.G4ArAJ.sZh6pE-mwO2qDAr1mfHEoo7EwbJb-TZT8h6nGg", + "Prefix": "!kn " + }, + "Bot": { + "910199451145076828": { + "MessageDeleteTimer": 2 + }, + "Technicians": [ + 240160344557879316 + ], + "WaitForRestart": 4, + "WaitForShutdown": 4 + }, + "Base": { + "910199451145076828": { + "MaxVoiceStateHours": 24, + "XpPerMessage": 2, + "XpPerOntimeHour": 4, + "AFKChannelIds": [ + 910199452915093593, + 910199452915093594 + ], + "AFKCommandChannelId": 910199452915093594, + "HelpCommandReferenceUrl": "https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot/wiki/Befehle" + } + }, + "BootLog": { + "910199451145076828": { + "LoginMessageChannelId": "910199452915093588" + } + }, + "Permission": { + "910199451145076828": { + "AdminRoleIds": [ + 925072155203477584 + ], + "ModeratorRoleIds": [ + 925072209884635167 + ] + } + } +} diff --git a/src/modules/moderator/command/user_info_command.py b/src/modules/moderator/command/user_group.py similarity index 94% rename from src/modules/moderator/command/user_info_command.py rename to src/modules/moderator/command/user_group.py index c83547ca..1988902b 100644 --- a/src/modules/moderator/command/user_info_command.py +++ b/src/modules/moderator/command/user_group.py @@ -18,7 +18,7 @@ from bot_data.abc.user_repository_abc import UserRepositoryABC from modules.permission.abc.permission_service_abc import PermissionServiceABC -class UserInfoCommand(DiscordCommandABC): +class UserGroup(DiscordCommandABC): def __init__( self, @@ -50,9 +50,14 @@ class UserInfoCommand(DiscordCommandABC): self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') - @commands.hybrid_command(name='user-info') + @commands.hybrid_group() @commands.guild_only() - async def user_info(self, ctx: Context, member: Optional[discord.Member] = None, *, wait: int = None): + async def user(self, ctx: Context): + pass + + @user.command() + @commands.guild_only() + async def info(self, ctx: Context, member: Optional[discord.Member] = None, *, wait: int = None): self._logger.debug(__name__, f'Received command user-info {ctx}:{member},{wait}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return diff --git a/src/modules/moderator/moderator_module.py b/src/modules/moderator/moderator_module.py index c981fc2d..7df1865f 100644 --- a/src/modules/moderator/moderator_module.py +++ b/src/modules/moderator/moderator_module.py @@ -6,7 +6,7 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from modules.moderator.command.purge_command import PurgeCommand -from modules.moderator.command.user_info_command import UserInfoCommand +from modules.moderator.command.user_group import UserGroup class ModeratorModule(ModuleABC): @@ -20,5 +20,5 @@ class ModeratorModule(ModuleABC): def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): # commands self._dc.add_command(PurgeCommand) - self._dc.add_command(UserInfoCommand) + self._dc.add_command(UserGroup) # events From 85f571762428420b6ef2161470df08ad89bf3bcb Mon Sep 17 00:00:00 2001 From: Nick Jungmann Date: Wed, 12 Oct 2022 21:27:55 +0200 Subject: [PATCH 002/275] Addad technician to PermissionService #60 --- .../permission/abc/permission_service_abc.py | 6 +++++ .../permission/service/permission_service.py | 27 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/modules/permission/abc/permission_service_abc.py b/src/modules/permission/abc/permission_service_abc.py index 3fc54ff5..b2323742 100644 --- a/src/modules/permission/abc/permission_service_abc.py +++ b/src/modules/permission/abc/permission_service_abc.py @@ -32,8 +32,14 @@ class PermissionServiceABC(ABC): @abstractmethod def get_moderators(self, g_id: int) -> list[discord.Member]: pass + @abstractmethod + def get_technicians(self) -> list[discord.Member]: pass + @abstractmethod def is_member_admin(self, member: discord.Member) -> bool: pass @abstractmethod def is_member_moderator(self, member: discord.Member) -> bool: pass + + @abstractmethod + def is_member_technician(self, member: discord.Member) -> bool: pass diff --git a/src/modules/permission/service/permission_service.py b/src/modules/permission/service/permission_service.py index f6376ccc..f43f2f69 100644 --- a/src/modules/permission/service/permission_service.py +++ b/src/modules/permission/service/permission_service.py @@ -3,13 +3,20 @@ from cpl_core.logging import LoggerABC from cpl_core.configuration import ConfigurationABC from cpl_discord.service import DiscordBotServiceABC +from bot_core.configuration.bot_settings import BotSettings from modules.permission.abc.permission_service_abc import PermissionServiceABC from modules.permission.configuration.permission_server_settings import PermissionServerSettings class PermissionService(PermissionServiceABC): - def __init__(self, logger: LoggerABC, bot: DiscordBotServiceABC, config: ConfigurationABC): + def __init__( + self, + logger: LoggerABC, + bot: DiscordBotServiceABC, + config: ConfigurationABC, + bot_settings: BotSettings + ): PermissionServiceABC.__init__(self) self._logger = logger self._bot = bot @@ -23,11 +30,20 @@ class PermissionService(PermissionServiceABC): self._moderator_roles: dict[int, list[discord.Role]] = {} self._moderators: dict[int, list[discord.Member]] = {} + self._technician_ids: list[int] = bot_settings.technicians + self._technicians: list[discord.Member] = [] + def on_ready(self): for guild in self._bot.guilds: guild: discord.Guild = guild self._logger.debug(__name__, f'Validate permission settings') + for technician_id in self._technician_ids: + technician = guild.get_member(technician_id) + if technician is None: + continue + self._technicians.append(technician) + settings: PermissionServerSettings = self._config.get_configuration(f'PermissionServerSettings_{guild.id}') if settings is None: self._logger.error(__name__, 'Permission settings not found') @@ -105,8 +121,15 @@ class PermissionService(PermissionServiceABC): def get_moderators(self, g_id: int) -> list[discord.Member]: return self._moderators[g_id] + def get_technicians(self) -> list[discord.Member]: + return self._technicians + def is_member_admin(self, member: discord.Member) -> bool: return member.guild.id in self._admins and member in self._admins[member.guild.id] def is_member_moderator(self, member: discord.Member) -> bool: - return member.guild.id in self._moderators and member in self._moderators[member.guild.id] or self.is_member_admin(member) + return member.guild.id in self._moderators \ + and member in self._moderators[member.guild.id] or self.is_member_admin(member) + + def is_member_technician(self, member: discord.Member) -> bool: + return member in self._technicians From d05dec3605e9e3ae526c9299cce329f2dc514f26 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 13 Oct 2022 16:34:04 +0200 Subject: [PATCH 003/275] Updated my config --- src/bot/config/appsettings.edrafts-lapi.json | 33 ++++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/bot/config/appsettings.edrafts-lapi.json b/src/bot/config/appsettings.edrafts-lapi.json index 8c26c3ea..9a4ccd18 100644 --- a/src/bot/config/appsettings.edrafts-lapi.json +++ b/src/bot/config/appsettings.edrafts-lapi.json @@ -1,10 +1,36 @@ { + "TimeFormatSettings": { + "DateFormat": "%Y-%m-%d", + "TimeFormat": "%H:%M:%S", + "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", + "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" + }, "LoggingSettings": { "Path": "logs/", - "Filename": "log_dev.log", - "ConsoleLogLevel": "DEBUG", + "Filename": "bot.log", + "ConsoleLogLevel": "TRACE", "FileLogLevel": "TRACE" }, + "BotLoggingSettings": { + "Command": { + "Path": "logs/", + "Filename": "commands.log", + "ConsoleLogLevel": "DEBUG", + "FileLogLevel": "TRACE" + }, + "Database": { + "Path": "logs/", + "Filename": "database.log", + "ConsoleLogLevel": "DEBUG", + "FileLogLevel": "TRACE" + }, + "Message": { + "Path": "logs/", + "Filename": "message.log", + "ConsoleLogLevel": "DEBUG", + "FileLogLevel": "TRACE" + } + }, "DatabaseSettings": { "Host": "localhost", "User": "kd_kdb", @@ -26,7 +52,8 @@ "Technicians": [ 240160344557879316 ], - "DeployFilesPath": "../../deploy" + "WaitForRestart": 4, + "WaitForShutdown": 4 }, "Base": { "910199451145076828": { From 0019f868ad8dbed10fbb3c0252f4aee228f0229e Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 13 Oct 2022 19:57:56 +0200 Subject: [PATCH 004/275] Added flask support #70 --- cpl-workspace.json | 4 +- src/bot/application.py | 19 ++++++- src/bot/config/appsettings.edrafts-lapi.json | 6 +++ src/bot/config/feature-flags.json | 1 + src/bot/module_list.py | 2 + src/bot/startup.py | 4 ++ src/bot_api/__init__.py | 1 + src/bot_api/api.py | 52 +++++++++++++++++++ src/bot_api/api_module.py | 26 ++++++++++ src/bot_api/api_thread.py | 27 ++++++++++ src/bot_api/bot-api.json | 48 +++++++++++++++++ src/bot_api/controller/__init__.py | 0 src/bot_api/controller/api_controller.py | 22 ++++++++ src/bot_api/controller/api_route.py | 0 src/bot_api/logging/__init__.py | 0 src/bot_api/logging/api_logger.py | 11 ++++ src/bot_api/route/__init__.py | 0 src/bot_api/route/route.py | 11 ++++ .../configuration/feature_flags_enum.py | 2 + .../configuration/feature_flags_settings.py | 2 + 20 files changed, 235 insertions(+), 3 deletions(-) create mode 100644 src/bot_api/__init__.py create mode 100644 src/bot_api/api.py create mode 100644 src/bot_api/api_module.py create mode 100644 src/bot_api/api_thread.py create mode 100644 src/bot_api/bot-api.json create mode 100644 src/bot_api/controller/__init__.py create mode 100644 src/bot_api/controller/api_controller.py create mode 100644 src/bot_api/controller/api_route.py create mode 100644 src/bot_api/logging/__init__.py create mode 100644 src/bot_api/logging/api_logger.py create mode 100644 src/bot_api/route/__init__.py create mode 100644 src/bot_api/route/route.py diff --git a/cpl-workspace.json b/cpl-workspace.json index 9841dd57..82fc9abe 100644 --- a/cpl-workspace.json +++ b/cpl-workspace.json @@ -11,13 +11,13 @@ "boot-log": "src/modules/boot_log/boot-log.json", "database": "src/modules/database/database.json", "moderator": "src/modules/moderator/moderator.json", - "permission": "src/modules/permission/permission.json" + "permission": "src/modules/permission/permission.json", + "bot-api": "src/bot_api/bot-api.json" }, "Scripts": { "prod": "export KDB_ENVIRONMENT=production; export KDB_NAME=KDB-Prod; cpl start;", "stage": "export KDB_ENVIRONMENT=staging; export KDB_NAME=KDB-Stage; cpl start;", "dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;", - "build-docker": "cpl b; docker-compose down; docker build -t kdb .", "compose": "docker-compose up -d", "docker": "cpl build-docker; cpl compose;" diff --git a/src/bot/application.py b/src/bot/application.py index 14ce439d..c557eace 100644 --- a/src/bot/application.py +++ b/src/bot/application.py @@ -7,6 +7,10 @@ from cpl_discord.configuration import DiscordBotSettings from cpl_discord.service import DiscordBotServiceABC, DiscordBotService from cpl_translation import TranslatePipe, TranslationServiceABC, TranslationSettings +from bot_api.api_thread import ApiThread +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings + class Application(DiscordBotApplicationABC): @@ -14,6 +18,7 @@ class Application(DiscordBotApplicationABC): DiscordBotApplicationABC.__init__(self, config, services) self._services = services + self._config = config # cpl-core self._logger: LoggerABC = services.get_service(LoggerABC) @@ -24,6 +29,12 @@ class Application(DiscordBotApplicationABC): self._translation: TranslationServiceABC = services.get_service(TranslationServiceABC) self._t: TranslatePipe = services.get_service(TranslatePipe) + self._feature_flags: FeatureFlagsSettings = config.get_configuration(FeatureFlagsSettings) + + # api + if self._feature_flags.get_flag(FeatureFlagsEnum.api_module): + self._api: ApiThread = services.get_service(ApiThread) + self._is_stopping = False async def configure(self): @@ -32,6 +43,12 @@ class Application(DiscordBotApplicationABC): async def main(self): try: self._logger.debug(__name__, f'Starting...') + if self._feature_flags.get_flag(FeatureFlagsEnum.api_module): + self._api.start() + + if self._feature_flags.get_flag(FeatureFlagsEnum.api_only): + return + self._logger.trace(__name__, f'Try to start {DiscordBotService.__name__}') await self._bot.start_async() await self._bot.stop_async() @@ -53,4 +70,4 @@ class Application(DiscordBotApplicationABC): Console.write_line() def is_restart(self): - return True if self._configuration.get_configuration('IS_RESTART') == 'true' else False# + return True if self._configuration.get_configuration('IS_RESTART') == 'true' else False # diff --git a/src/bot/config/appsettings.edrafts-lapi.json b/src/bot/config/appsettings.edrafts-lapi.json index 9a4ccd18..2e9e52a3 100644 --- a/src/bot/config/appsettings.edrafts-lapi.json +++ b/src/bot/config/appsettings.edrafts-lapi.json @@ -12,6 +12,12 @@ "FileLogLevel": "TRACE" }, "BotLoggingSettings": { + "Api": { + "Path": "logs/", + "Filename": "api.log", + "ConsoleLogLevel": "TRACE", + "FileLogLevel": "TRACE" + }, "Command": { "Path": "logs/", "Filename": "commands.log", diff --git a/src/bot/config/feature-flags.json b/src/bot/config/feature-flags.json index b20c9cca..68bf6e00 100644 --- a/src/bot/config/feature-flags.json +++ b/src/bot/config/feature-flags.json @@ -1,5 +1,6 @@ { "FeatureFlags": { + "ApiModule": true, "AdminModule": true, "AutoRoleModule": true, "BaseModule": true, diff --git a/src/bot/module_list.py b/src/bot/module_list.py index 5b8f4e5b..866b7d67 100644 --- a/src/bot/module_list.py +++ b/src/bot/module_list.py @@ -1,5 +1,6 @@ from cpl_query.extension import List +from bot_api.api_module import ApiModule from bot_core.core_extension.core_extension_module import CoreExtensionModule from bot_core.core_module import CoreModule from bot_data.data_module import DataModule @@ -20,6 +21,7 @@ class ModuleList: return List(type, [ CoreModule, # has to be first! DataModule, + ApiModule, AdminModule, AutoRoleModule, BaseModule, diff --git a/src/bot/startup.py b/src/bot/startup.py index f1c123c5..1384fbd9 100644 --- a/src/bot/startup.py +++ b/src/bot/startup.py @@ -9,6 +9,7 @@ from cpl_core.dependency_injection import ServiceProviderABC from cpl_core.environment import ApplicationEnvironment from cpl_core.logging import LoggerABC +from bot_api.logging.api_logger import ApiLogger from bot_core.abc.custom_file_logger_abc import CustomFileLoggerABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings @@ -40,6 +41,9 @@ class Startup(StartupABC): services.add_singleton(CustomFileLoggerABC, DatabaseLogger) services.add_singleton(CustomFileLoggerABC, MessageLogger) + if self._feature_flags.get_flag(FeatureFlagsEnum.api_module): + services.add_singleton(CustomFileLoggerABC, ApiLogger) + services.add_translation() services.add_db_context(DBContext, self._config.get_configuration(DatabaseSettings)) diff --git a/src/bot_api/__init__.py b/src/bot_api/__init__.py new file mode 100644 index 00000000..ad5eca30 --- /dev/null +++ b/src/bot_api/__init__.py @@ -0,0 +1 @@ +# imports: diff --git a/src/bot_api/api.py b/src/bot_api/api.py new file mode 100644 index 00000000..7a75e2a4 --- /dev/null +++ b/src/bot_api/api.py @@ -0,0 +1,52 @@ +import sys +from functools import partial + +from cpl_core.dependency_injection import ServiceProviderABC +from flask import Flask, request + +from bot_api.logging.api_logger import ApiLogger +from bot_api.route.route import Route + + +class Api(Flask): + + def __init__( + self, + logger: ApiLogger, + services: ServiceProviderABC, + *args, **kwargs + ): + if not args: + kwargs.setdefault('import_name', __name__) + + Flask.__init__(self, *args, **kwargs) + + self._logger = logger + self._services = services + + # register before request + self.before_request_funcs.setdefault(None, []).append(self.before_request) + + def _register_routes(self): + for path, f in Route.registered_routes.items(): + cls = None + qual_name_split = f.__qualname__.split('.') + if len(qual_name_split) > 0: + cls_type = vars(sys.modules[f.__module__])[qual_name_split[0]] + cls = self._services.get_service(cls_type) + + partial_f = partial(f, self if cls is None else cls) + partial_f.__name__ = f.__name__ + self.route(path)(partial_f) + + def before_request(self, *args, **kwargs): + self._logger.debug(__name__, f'Received GET @{request.url}') + headers = str(request.headers).replace("\n", "\n\t") + self._logger.trace(__name__, f'Headers: \n\t{headers}') + self._logger.trace(__name__, f'Body: {request.get_json(force=True, silent=True)}') + + def start(self): + self._logger.info(__name__, f'Starting API') + self._register_routes() + from waitress import serve + serve(self, host="0.0.0.0", port=5000) diff --git a/src/bot_api/api_module.py b/src/bot_api/api_module.py new file mode 100644 index 00000000..91f91794 --- /dev/null +++ b/src/bot_api/api_module.py @@ -0,0 +1,26 @@ +from cpl_core.configuration import ConfigurationABC +from cpl_core.dependency_injection import ServiceCollectionABC +from cpl_core.environment import ApplicationEnvironmentABC +from cpl_discord.service.discord_collection_abc import DiscordCollectionABC +from flask import Flask + +from bot_api.api import Api +from bot_api.api_thread import ApiThread +from bot_api.controller.api_controller import ApiController +from bot_core.abc.module_abc import ModuleABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum + + +class ApiModule(ModuleABC): + + def __init__(self, dc: DiscordCollectionABC): + ModuleABC.__init__(self, dc, FeatureFlagsEnum.api_module) + + def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): + pass + + def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): + services.add_singleton(ApiThread) + services.add_singleton(Flask, Api) + + services.add_transient(ApiController) diff --git a/src/bot_api/api_thread.py b/src/bot_api/api_thread.py new file mode 100644 index 00000000..b6a34bde --- /dev/null +++ b/src/bot_api/api_thread.py @@ -0,0 +1,27 @@ +import threading + +from bot_api.api import Api +from bot_api.logging.api_logger import ApiLogger +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings + + +class ApiThread(threading.Thread): + + def __init__( + self, + logger: ApiLogger, + api: Api, + feature_flags: FeatureFlagsSettings + ): + threading.Thread.__init__(self, daemon=not feature_flags.get_flag(FeatureFlagsEnum.api_only)) + + self._logger = logger + self._api = api + + def run(self) -> None: + try: + self._logger.trace(__name__, f'Try to start {type(self._api).__name__}') + self._api.start() + except Exception as e: + self._logger.error(__name__, 'Start failed', e) diff --git a/src/bot_api/bot-api.json b/src/bot_api/bot-api.json new file mode 100644 index 00000000..b31234fd --- /dev/null +++ b/src/bot_api/bot-api.json @@ -0,0 +1,48 @@ +{ + "ProjectSettings": { + "Name": "bot-api", + "Version": { + "Major": "0", + "Minor": "0", + "Micro": "0" + }, + "Author": "", + "AuthorEmail": "", + "Description": "", + "LongDescription": "", + "URL": "", + "CopyrightDate": "", + "CopyrightName": "", + "LicenseName": "", + "LicenseDescription": "", + "Dependencies": [ + "cpl-core==2022.10.0.post6", + "Flask==2.2.2", + "Flask-Classful==0.14.2" + ], + "DevDependencies": [ + "cpl-cli>=2022.10.0" + ], + "PythonVersion": ">=3.10.4", + "PythonPath": { + "linux": "" + }, + "Classifiers": [] + }, + "BuildSettings": { + "ProjectType": "library", + "SourcePath": "", + "OutputPath": "../../dist", + "Main": "bot_api.main", + "EntryPoint": "bot-api", + "IncludePackageData": false, + "Included": [], + "Excluded": [ + "*/__pycache__", + "*/logs", + "*/tests" + ], + "PackageData": {}, + "ProjectReferences": [] + } +} \ No newline at end of file diff --git a/src/bot_api/controller/__init__.py b/src/bot_api/controller/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/bot_api/controller/api_controller.py b/src/bot_api/controller/api_controller.py new file mode 100644 index 00000000..9c714166 --- /dev/null +++ b/src/bot_api/controller/api_controller.py @@ -0,0 +1,22 @@ +from cpl_translation import TranslatePipe + +from bot_api.api import Api +from bot_api.logging.api_logger import ApiLogger +from bot_api.route.route import Route + + +class ApiController: + + def __init__( + self, + logger: ApiLogger, + t: TranslatePipe, + api: Api + ): + self._logger = logger + self._t = t + self._api = api + + @Route.route('/api/hello-world') + def hello_world(self): + return self._t.transform('common.hello_world') diff --git a/src/bot_api/controller/api_route.py b/src/bot_api/controller/api_route.py new file mode 100644 index 00000000..e69de29b diff --git a/src/bot_api/logging/__init__.py b/src/bot_api/logging/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/bot_api/logging/api_logger.py b/src/bot_api/logging/api_logger.py new file mode 100644 index 00000000..1e4f00e8 --- /dev/null +++ b/src/bot_api/logging/api_logger.py @@ -0,0 +1,11 @@ +from cpl_core.configuration import ConfigurationABC +from cpl_core.environment import ApplicationEnvironmentABC +from cpl_core.time import TimeFormatSettings + +from bot_core.abc.custom_file_logger_abc import CustomFileLoggerABC + + +class ApiLogger(CustomFileLoggerABC): + + def __init__(self, config: ConfigurationABC, time_format: TimeFormatSettings, env: ApplicationEnvironmentABC): + CustomFileLoggerABC.__init__(self, 'Api', config, time_format, env) diff --git a/src/bot_api/route/__init__.py b/src/bot_api/route/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/bot_api/route/route.py b/src/bot_api/route/route.py new file mode 100644 index 00000000..ef0e3994 --- /dev/null +++ b/src/bot_api/route/route.py @@ -0,0 +1,11 @@ +class Route: + registered_routes = {} + + @classmethod + def route(cls, path=None): + # simple decorator for class based views + def inner(fn): + cls.registered_routes[path] = fn + return fn + + return inner diff --git a/src/bot_core/configuration/feature_flags_enum.py b/src/bot_core/configuration/feature_flags_enum.py index dac193b1..b41ec6e5 100644 --- a/src/bot_core/configuration/feature_flags_enum.py +++ b/src/bot_core/configuration/feature_flags_enum.py @@ -4,6 +4,7 @@ from enum import Enum class FeatureFlagsEnum(Enum): # modules + api_module = 'ApiModule' admin_module = 'AdminModule' auto_role_module = 'AutoRoleModule' base_module = 'BaseModule' @@ -15,4 +16,5 @@ class FeatureFlagsEnum(Enum): moderator_module = 'ModeratorModule' permission_module = 'PermissionModule' # features + api_only = 'ApiOnly' presence = 'Presence' diff --git a/src/bot_core/configuration/feature_flags_settings.py b/src/bot_core/configuration/feature_flags_settings.py index 4556e784..1861f9a1 100644 --- a/src/bot_core/configuration/feature_flags_settings.py +++ b/src/bot_core/configuration/feature_flags_settings.py @@ -14,6 +14,7 @@ class FeatureFlagsSettings(ConfigurationModelABC): self._flags = { # modules + FeatureFlagsEnum.api_module.value: False, # 13.10.2022 #70 FeatureFlagsEnum.admin_module.value: False, # 02.10.2022 #48 FeatureFlagsEnum.auto_role_module.value: True, # 03.10.2022 #54 FeatureFlagsEnum.base_module.value: True, # 02.10.2022 #48 @@ -25,6 +26,7 @@ class FeatureFlagsSettings(ConfigurationModelABC): FeatureFlagsEnum.moderator_module.value: False, # 02.10.2022 #48 FeatureFlagsEnum.permission_module.value: True, # 02.10.2022 #48 # features + FeatureFlagsEnum.api_only.value: False, # 13.10.2022 #70 FeatureFlagsEnum.presence.value: True, # 03.10.2022 #56 } From 0927288bb5d0f237bc528dc33efc3492471ae895 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 13 Oct 2022 20:57:47 +0200 Subject: [PATCH 005/275] Added api calls #70 --- src/bot/bot.json | 1 + src/bot/translation/de.json | 8 +++ src/bot_api/__init__.py | 25 +++++++ src/bot_api/abc/__init__.py | 26 +++++++ src/bot_api/abc/dto_abc.py | 13 ++++ src/bot_api/api_module.py | 12 +++- .../config/apisettings.development.json | 8 +++ .../config/apisettings.edrafts-lapi.json | 1 + .../config/apisettings.edrafts-pc-ubuntu.json | 1 + src/bot_api/config/apisettings.json | 1 + .../config/apisettings.production.json | 1 + src/bot_api/config/apisettings.staging.json | 1 + src/bot_api/config/appsettings.PC-Nick.json | 1 + src/bot_api/configuration/__init__.py | 1 + src/bot_api/configuration/api_settings.py | 23 ++++++ src/bot_api/configuration/version_settings.py | 55 ++++++++++++++ src/bot_api/controller/__init__.py | 26 +++++++ src/bot_api/controller/api_controller.py | 58 +++++++++++++-- src/bot_api/controller/api_route.py | 0 src/bot_api/logging/__init__.py | 26 +++++++ src/bot_api/model/__init__.py | 26 +++++++ src/bot_api/model/settings_dto.py | 71 +++++++++++++++++++ src/bot_api/model/version_dto.py | 27 +++++++ src/bot_api/route/__init__.py | 26 +++++++ 24 files changed, 433 insertions(+), 5 deletions(-) create mode 100644 src/bot_api/abc/__init__.py create mode 100644 src/bot_api/abc/dto_abc.py create mode 100644 src/bot_api/config/apisettings.development.json create mode 100644 src/bot_api/config/apisettings.edrafts-lapi.json create mode 100644 src/bot_api/config/apisettings.edrafts-pc-ubuntu.json create mode 100644 src/bot_api/config/apisettings.json create mode 100644 src/bot_api/config/apisettings.production.json create mode 100644 src/bot_api/config/apisettings.staging.json create mode 100644 src/bot_api/config/appsettings.PC-Nick.json create mode 100644 src/bot_api/configuration/__init__.py create mode 100644 src/bot_api/configuration/api_settings.py create mode 100644 src/bot_api/configuration/version_settings.py delete mode 100644 src/bot_api/controller/api_route.py create mode 100644 src/bot_api/model/__init__.py create mode 100644 src/bot_api/model/settings_dto.py create mode 100644 src/bot_api/model/version_dto.py diff --git a/src/bot/bot.json b/src/bot/bot.json index 8bbcdf43..64495674 100644 --- a/src/bot/bot.json +++ b/src/bot/bot.json @@ -45,6 +45,7 @@ ], "PackageData": {}, "ProjectReferences": [ + "../bot_api/bot-api.json", "../bot_core/bot-core.json", "../bot_data/bot-data.json", "../modules/base/base.json", diff --git a/src/bot/translation/de.json b/src/bot/translation/de.json index 910de717..03b69e95 100644 --- a/src/bot/translation/de.json +++ b/src/bot/translation/de.json @@ -152,5 +152,13 @@ "database": {}, "permission": { } + }, + "api": { + "api": { + "test_mail": { + "subject": "Krümmelmonster Web Interface Test-Mail", + "message": "Dies ist eine Test-Mail vom Krümmelmonster Web Interface\nGesendet von {}-{}" + } + } } } \ No newline at end of file diff --git a/src/bot_api/__init__.py b/src/bot_api/__init__.py index ad5eca30..99097c73 100644 --- a/src/bot_api/__init__.py +++ b/src/bot_api/__init__.py @@ -1 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'bot_api' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.2.3' + +from collections import namedtuple + + # imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='2', micro='3') diff --git a/src/bot_api/abc/__init__.py b/src/bot_api/abc/__init__.py new file mode 100644 index 00000000..38bf68b7 --- /dev/null +++ b/src/bot_api/abc/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'bot_api.abc' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.2.3' + +from collections import namedtuple + + +# imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='2', micro='3') diff --git a/src/bot_api/abc/dto_abc.py b/src/bot_api/abc/dto_abc.py new file mode 100644 index 00000000..58b458ad --- /dev/null +++ b/src/bot_api/abc/dto_abc.py @@ -0,0 +1,13 @@ +from abc import ABC, abstractmethod + + +class DtoABC(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + def from_dict(self, values: dict): pass + + @abstractmethod + def to_dict(self) -> dict: pass diff --git a/src/bot_api/api_module.py b/src/bot_api/api_module.py index 91f91794..3f26d80c 100644 --- a/src/bot_api/api_module.py +++ b/src/bot_api/api_module.py @@ -1,6 +1,9 @@ +import os + from cpl_core.configuration import ConfigurationABC from cpl_core.dependency_injection import ServiceCollectionABC from cpl_core.environment import ApplicationEnvironmentABC +from cpl_core.mailing import EMailClientABC, EMailClient from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from flask import Flask @@ -17,9 +20,16 @@ class ApiModule(ModuleABC): ModuleABC.__init__(self, dc, FeatureFlagsEnum.api_module) def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): - pass + cwd = env.working_directory + env.set_working_directory(os.path.dirname(os.path.realpath(__file__))) + config.add_json_file(f'config/apisettings.json', optional=False) + config.add_json_file(f'config/apisettings.{env.environment_name}.json', optional=True) + config.add_json_file(f'config/apisettings.{env.host_name}.json', optional=True) + env.set_working_directory(cwd) def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): + services.add_singleton(EMailClientABC, EMailClient) + services.add_singleton(ApiThread) services.add_singleton(Flask, Api) diff --git a/src/bot_api/config/apisettings.development.json b/src/bot_api/config/apisettings.development.json new file mode 100644 index 00000000..534e5087 --- /dev/null +++ b/src/bot_api/config/apisettings.development.json @@ -0,0 +1,8 @@ +{ + "EMailClientSettings": { + "Host": "mail.sh-edraft.de", + "Port": "587", + "UserName": "dev-srv@sh-edraft.de", + "Credentials": "RmBOQX1eNFYiYjgsSid3fV1nelc2WA==" + } +} \ No newline at end of file diff --git a/src/bot_api/config/apisettings.edrafts-lapi.json b/src/bot_api/config/apisettings.edrafts-lapi.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/src/bot_api/config/apisettings.edrafts-lapi.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/bot_api/config/apisettings.edrafts-pc-ubuntu.json b/src/bot_api/config/apisettings.edrafts-pc-ubuntu.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/src/bot_api/config/apisettings.edrafts-pc-ubuntu.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/bot_api/config/apisettings.json b/src/bot_api/config/apisettings.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/src/bot_api/config/apisettings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/bot_api/config/apisettings.production.json b/src/bot_api/config/apisettings.production.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/src/bot_api/config/apisettings.production.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/bot_api/config/apisettings.staging.json b/src/bot_api/config/apisettings.staging.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/src/bot_api/config/apisettings.staging.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/bot_api/config/appsettings.PC-Nick.json b/src/bot_api/config/appsettings.PC-Nick.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/src/bot_api/config/appsettings.PC-Nick.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/bot_api/configuration/__init__.py b/src/bot_api/configuration/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/src/bot_api/configuration/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/src/bot_api/configuration/api_settings.py b/src/bot_api/configuration/api_settings.py new file mode 100644 index 00000000..e50f25fa --- /dev/null +++ b/src/bot_api/configuration/api_settings.py @@ -0,0 +1,23 @@ +import traceback + +from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC +from cpl_core.console import Console + + +class APISettings(ConfigurationModelABC): + + def __init__(self): + ConfigurationModelABC.__init__(self) + + self._redirect_to_https = False + + @property + def redirect_to_https(self) -> bool: + return self._redirect_to_https + + def from_dict(self, settings: dict): + try: + self._redirect_to_https = bool(settings['RedirectToHTTPS']) + except Exception as e: + Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') + Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') diff --git a/src/bot_api/configuration/version_settings.py b/src/bot_api/configuration/version_settings.py new file mode 100644 index 00000000..ef1af9b6 --- /dev/null +++ b/src/bot_api/configuration/version_settings.py @@ -0,0 +1,55 @@ +from typing import Optional + +from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC +from cpl_cli.configuration.version_settings_name_enum import VersionSettingsNameEnum + + +class VersionSettings(ConfigurationModelABC): + + def __init__( + self, + major: str = None, + minor: str = None, + micro: str = None + ): + ConfigurationModelABC.__init__(self) + + self._major: Optional[str] = major + self._minor: Optional[str] = minor + self._micro: Optional[str] = micro + + @property + def major(self) -> str: + return self._major + + @property + def minor(self) -> str: + return self._minor + + @property + def micro(self) -> str: + return self._micro + + def to_str(self) -> str: + if self._micro is None: + return f'{self._major}.{self._minor}' + else: + return f'{self._major}.{self._minor}.{self._micro}' + + def from_dict(self, settings: dict): + self._major = settings[VersionSettingsNameEnum.major.value] + self._minor = settings[VersionSettingsNameEnum.minor.value] + micro = settings[VersionSettingsNameEnum.micro.value] + if micro != '': + self._micro = micro + + def to_dict(self) -> dict: + version = { + VersionSettingsNameEnum.major.value: self._major, + VersionSettingsNameEnum.minor.value: self._minor, + } + + if self._micro is not None: + version[VersionSettingsNameEnum.micro.value] = self._micro + + return version diff --git a/src/bot_api/controller/__init__.py b/src/bot_api/controller/__init__.py index e69de29b..44c37e5c 100644 --- a/src/bot_api/controller/__init__.py +++ b/src/bot_api/controller/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'bot_api.controller' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.2.3' + +from collections import namedtuple + + +# imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='2', micro='3') diff --git a/src/bot_api/controller/api_controller.py b/src/bot_api/controller/api_controller.py index 9c714166..e17103e6 100644 --- a/src/bot_api/controller/api_controller.py +++ b/src/bot_api/controller/api_controller.py @@ -1,7 +1,14 @@ +import os + +from cpl_core.configuration import ConfigurationABC +from cpl_core.environment import ApplicationEnvironmentABC +from cpl_core.mailing import EMail, EMailClientABC, EMailClientSettings from cpl_translation import TranslatePipe from bot_api.api import Api from bot_api.logging.api_logger import ApiLogger +from bot_api.model.settings_dto import SettingsDTO +from bot_api.model.version_dto import VersionDTO from bot_api.route.route import Route @@ -9,14 +16,57 @@ class ApiController: def __init__( self, + config: ConfigurationABC, + env: ApplicationEnvironmentABC, logger: ApiLogger, t: TranslatePipe, - api: Api + api: Api, + mail_settings: EMailClientSettings, + mailer: EMailClientABC ): + self._config = config + self._env = env self._logger = logger self._t = t self._api = api + self._mail_settings = mail_settings + self._mailer = mailer - @Route.route('/api/hello-world') - def hello_world(self): - return self._t.transform('common.hello_world') + @Route.route('/api/api-version') + def api_version(self): + import bot_api + version = bot_api.version_info + return VersionDTO(version.major, version.minor, version.micro).to_dict() + + @Route.route('/api/settings') + def settings(self): + # TODO: Authentication + import bot_api + version = bot_api.version_info + + return SettingsDTO( + '', + VersionDTO(version.major, version.minor, version.micro), + os.path.abspath(os.path.join(self._env.working_directory, 'config')), + '', + '/', + 0, + 0, + self._mail_settings.user_name, + self._mail_settings.port, + self._mail_settings.host, + self._mail_settings.user_name, + self._mail_settings.user_name, + ).to_dict() + + @Route.route('/api/send-test-mail/') + def send_test_mail(self, email: str): + # TODO: Authentication + mail = EMail() + mail.add_header('Mime-Version: 1.0') + mail.add_header('Content-Type: text/plain; charset=utf-8') + mail.add_header('Content-Transfer-Encoding: quoted-printable') + mail.add_receiver(email) + mail.subject = self._t.transform('api.api.test_mail.subject') + mail.body = self._t.transform('api.api.test_mail.message').format(self._env.host_name, self._env.environment_name) + self._mailer.send_mail(mail) diff --git a/src/bot_api/controller/api_route.py b/src/bot_api/controller/api_route.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/bot_api/logging/__init__.py b/src/bot_api/logging/__init__.py index e69de29b..d7b9d180 100644 --- a/src/bot_api/logging/__init__.py +++ b/src/bot_api/logging/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'bot_api.logging' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.2.3' + +from collections import namedtuple + + +# imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='2', micro='3') diff --git a/src/bot_api/model/__init__.py b/src/bot_api/model/__init__.py new file mode 100644 index 00000000..da73f223 --- /dev/null +++ b/src/bot_api/model/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'bot_api.model' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.2.3' + +from collections import namedtuple + + +# imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='2', micro='3') diff --git a/src/bot_api/model/settings_dto.py b/src/bot_api/model/settings_dto.py new file mode 100644 index 00000000..6d72245b --- /dev/null +++ b/src/bot_api/model/settings_dto.py @@ -0,0 +1,71 @@ +import traceback + +from cpl_core.console import Console + +from bot_api.abc.dto_abc import DtoABC +from bot_api.model.version_dto import VersionDTO + + +class SettingsDTO(DtoABC): + + def __init__( + self, + web_version: str, + api_version: VersionDTO, + config_path: str, + web_base_url: str, + api_base_url: str, + token_expire_time: int, + refresh_token_expire_time: int, + mail_user: str, + mail_port: int, + mail_host: str, + mail_transceiver: str, + mail_transceiver_address: str, + ): + DtoABC.__init__(self) + + self._web_version = '' + self._api_version = VersionDTO() + self._config_path = '' + self._web_base_url = '' + self._api_base_url = '' + + self._token_expire_time = 0 + self._refresh_token_expire_time = 0 + + self._mail_user = '' + self._mail_port = 0 + self._mail_host = '' + self._mail_transceiver = '' + self._mail_transceiver_address = '' + + def from_dict(self, values: dict): + self._web_version = values['WebVersion'] + self._api_version.from_dict(values['ApiVersion']) + self._config_path = values['ConfigPath'] + self._web_base_url = values['WebBaseURL'] + self._api_base_url = values['ApiBaseURL'] + self._token_expire_time = values['TokenExpireTime'] + self._refresh_token_expire_time = values['RefreshTokenExpireTime'] + self._mail_user = values['MailUser'] + self._mail_port = values['MailPort'] + self._mail_host = values['MailHost'] + self._mail_transceiver = values['MailTransceiver'] + self._mail_transceiver_address = values['MailTransceiverAddress'] + + def to_dict(self) -> dict: + return { + 'WebVersion': self._web_version, + 'ApiVersion': self._api_version.to_dict(), + 'ConfigPath': self._config_path, + 'WebBaseURL': self._web_base_url, + 'ApiBaseURL': self._api_base_url, + 'TokenExpireTime': self._token_expire_time, + 'RefreshTokenExpireTime': self._refresh_token_expire_time, + 'MailUser': self._mail_user, + 'MailPort': self._mail_port, + 'MailHost': self._mail_host, + 'MailTransceiver': self._mail_transceiver, + 'MailTransceiverAddress': self._mail_transceiver_address, + } diff --git a/src/bot_api/model/version_dto.py b/src/bot_api/model/version_dto.py new file mode 100644 index 00000000..8ef98dc4 --- /dev/null +++ b/src/bot_api/model/version_dto.py @@ -0,0 +1,27 @@ +import traceback + +from cpl_core.console import Console + +from bot_api.abc.dto_abc import DtoABC + + +class VersionDTO(DtoABC): + + def __init__(self, major: str = None, minor: str = None, micro: str = None): + DtoABC.__init__(self) + + self._major = major + self._minor = minor + self._micro = micro + + def from_dict(self, values: dict): + self._major = values['Major'] + self._minor = values['Minor'] + self._micro = values['Micro'] + + def to_dict(self) -> dict: + return { + 'Major': self._major, + 'Minor': self._minor, + 'Micro': self._micro, + } diff --git a/src/bot_api/route/__init__.py b/src/bot_api/route/__init__.py index e69de29b..3f08538e 100644 --- a/src/bot_api/route/__init__.py +++ b/src/bot_api/route/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'bot_api.route' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.2.3' + +from collections import namedtuple + + +# imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='2', micro='3') From aab3d6236533282517d9fc58a844b738c35a904d Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 14 Oct 2022 07:04:56 +0200 Subject: [PATCH 006/275] Added api settings #70 --- src/bot/config/feature-flags.json | 3 +- src/bot_api/api.py | 7 ++- .../config/apisettings.edrafts-lapi.json | 18 ++++++- src/bot_api/configuration/api_settings.py | 14 +++++- .../configuration/authentication_settings.py | 48 +++++++++++++++++++ .../configuration/frontend_settings.py | 23 +++++++++ 6 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 src/bot_api/configuration/authentication_settings.py create mode 100644 src/bot_api/configuration/frontend_settings.py diff --git a/src/bot/config/feature-flags.json b/src/bot/config/feature-flags.json index 68bf6e00..2d214f7f 100644 --- a/src/bot/config/feature-flags.json +++ b/src/bot/config/feature-flags.json @@ -10,6 +10,7 @@ "DatabaseModule": true, "ModeratorModule": true, "PermissionModule": true, - "PresenceModule": true + "PresenceModule": true, + "ApiOnly": true } } diff --git a/src/bot_api/api.py b/src/bot_api/api.py index 7a75e2a4..c4043505 100644 --- a/src/bot_api/api.py +++ b/src/bot_api/api.py @@ -4,6 +4,7 @@ from functools import partial from cpl_core.dependency_injection import ServiceProviderABC from flask import Flask, request +from bot_api.configuration.api_settings import ApiSettings from bot_api.logging.api_logger import ApiLogger from bot_api.route.route import Route @@ -14,6 +15,7 @@ class Api(Flask): self, logger: ApiLogger, services: ServiceProviderABC, + api_settings: ApiSettings, *args, **kwargs ): if not args: @@ -23,6 +25,7 @@ class Api(Flask): self._logger = logger self._services = services + self._apt_settings = api_settings # register before request self.before_request_funcs.setdefault(None, []).append(self.before_request) @@ -46,7 +49,7 @@ class Api(Flask): self._logger.trace(__name__, f'Body: {request.get_json(force=True, silent=True)}') def start(self): - self._logger.info(__name__, f'Starting API') + self._logger.info(__name__, f'Starting API {self._apt_settings.host}:{self._apt_settings.port}') self._register_routes() from waitress import serve - serve(self, host="0.0.0.0", port=5000) + serve(self, host=self._apt_settings.host, port=self._apt_settings.port) diff --git a/src/bot_api/config/apisettings.edrafts-lapi.json b/src/bot_api/config/apisettings.edrafts-lapi.json index 9e26dfee..fb7c257c 100644 --- a/src/bot_api/config/apisettings.edrafts-lapi.json +++ b/src/bot_api/config/apisettings.edrafts-lapi.json @@ -1 +1,17 @@ -{} \ No newline at end of file +{ + "Api": { + "Port": 5000, + "Host": "0.0.0.0", + "RedirectToHTTPS": false + }, + "Authentication": { + "SecretKey": "F3b5LDz+#Jvzg=W!@gsa%xsF", + "Issuer": "http://localhost:5000", + "Audience": "http://localhost:5000", + "TokenExpireTime": 1, + "RefreshTokenExpireTime": 7 + }, + "Frontend": { + "URL": "http://localhost:4200/" + } +} \ No newline at end of file diff --git a/src/bot_api/configuration/api_settings.py b/src/bot_api/configuration/api_settings.py index e50f25fa..237a272e 100644 --- a/src/bot_api/configuration/api_settings.py +++ b/src/bot_api/configuration/api_settings.py @@ -4,19 +4,31 @@ from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC from cpl_core.console import Console -class APISettings(ConfigurationModelABC): +class ApiSettings(ConfigurationModelABC): def __init__(self): ConfigurationModelABC.__init__(self) + self._port = 80 + self._host = '' self._redirect_to_https = False + @property + def port(self) -> int: + return self._port + + @property + def host(self) -> str: + return self._host + @property def redirect_to_https(self) -> bool: return self._redirect_to_https def from_dict(self, settings: dict): try: + self._port = int(settings['Port']) + self._host = settings['Host'] self._redirect_to_https = bool(settings['RedirectToHTTPS']) except Exception as e: Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') diff --git a/src/bot_api/configuration/authentication_settings.py b/src/bot_api/configuration/authentication_settings.py new file mode 100644 index 00000000..9a573a8b --- /dev/null +++ b/src/bot_api/configuration/authentication_settings.py @@ -0,0 +1,48 @@ +import traceback +from datetime import datetime + +from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC +from cpl_core.console import Console + + +class AuthenticationSettings(ConfigurationModelABC): + + def __init__(self): + ConfigurationModelABC.__init__(self) + + self._secret_key = '' + self._issuer = '' + self._audience = '' + self._token_expire_time = 0 + self._refresh_token_expire_time = 0 + + @property + def secret_key(self) -> str: + return self._secret_key + + @property + def issuer(self) -> str: + return self._issuer + + @property + def audience(self) -> str: + return self._audience + + @property + def token_expire_time(self) -> int: + return self._token_expire_time + + @property + def refresh_token_expire_time(self) -> int: + return self._refresh_token_expire_time + + def from_dict(self, settings: dict): + try: + self._secret_key = settings['SecretKey'] + self._issuer = settings['Issuer'] + self._audience = settings['Audience'] + self._token_expire_time = int(settings['TokenExpireTime']) + self._refresh_token_expire_time = int(settings['RefreshTokenExpireTime']) + except Exception as e: + Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') + Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') diff --git a/src/bot_api/configuration/frontend_settings.py b/src/bot_api/configuration/frontend_settings.py new file mode 100644 index 00000000..2090f5a2 --- /dev/null +++ b/src/bot_api/configuration/frontend_settings.py @@ -0,0 +1,23 @@ +import traceback + +from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC +from cpl_core.console import Console + + +class FrontendSettings(ConfigurationModelABC): + + def __init__(self): + ConfigurationModelABC.__init__(self) + + self._url = '' + + @property + def url(self) -> str: + return self._url + + def from_dict(self, settings: dict): + try: + self._url = settings['URL'] + except Exception as e: + Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') + Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') From e8c491a47836ff6185e402a72dd43d70bafd292a Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 14 Oct 2022 07:16:12 +0200 Subject: [PATCH 007/275] Made api async #70 --- src/bot/application.py | 1 + src/bot_api/api_thread.py | 2 +- src/bot_api/bot-api.json | 2 +- src/bot_api/controller/api_controller.py | 6 +++--- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/bot/application.py b/src/bot/application.py index c557eace..726cdbdb 100644 --- a/src/bot/application.py +++ b/src/bot/application.py @@ -47,6 +47,7 @@ class Application(DiscordBotApplicationABC): self._api.start() if self._feature_flags.get_flag(FeatureFlagsEnum.api_only): + self._api.join() return self._logger.trace(__name__, f'Try to start {DiscordBotService.__name__}') diff --git a/src/bot_api/api_thread.py b/src/bot_api/api_thread.py index b6a34bde..f3ecf6cc 100644 --- a/src/bot_api/api_thread.py +++ b/src/bot_api/api_thread.py @@ -14,7 +14,7 @@ class ApiThread(threading.Thread): api: Api, feature_flags: FeatureFlagsSettings ): - threading.Thread.__init__(self, daemon=not feature_flags.get_flag(FeatureFlagsEnum.api_only)) + threading.Thread.__init__(self, daemon=True) self._logger = logger self._api = api diff --git a/src/bot_api/bot-api.json b/src/bot_api/bot-api.json index b31234fd..09e41293 100644 --- a/src/bot_api/bot-api.json +++ b/src/bot_api/bot-api.json @@ -17,7 +17,7 @@ "LicenseDescription": "", "Dependencies": [ "cpl-core==2022.10.0.post6", - "Flask==2.2.2", + "Flask[async]==2.2.2", "Flask-Classful==0.14.2" ], "DevDependencies": [ diff --git a/src/bot_api/controller/api_controller.py b/src/bot_api/controller/api_controller.py index e17103e6..1b7ae669 100644 --- a/src/bot_api/controller/api_controller.py +++ b/src/bot_api/controller/api_controller.py @@ -33,13 +33,13 @@ class ApiController: self._mailer = mailer @Route.route('/api/api-version') - def api_version(self): + async def api_version(self): import bot_api version = bot_api.version_info return VersionDTO(version.major, version.minor, version.micro).to_dict() @Route.route('/api/settings') - def settings(self): + async def settings(self): # TODO: Authentication import bot_api version = bot_api.version_info @@ -60,7 +60,7 @@ class ApiController: ).to_dict() @Route.route('/api/send-test-mail/') - def send_test_mail(self, email: str): + async def send_test_mail(self, email: str): # TODO: Authentication mail = EMail() mail.add_header('Mime-Version: 1.0') From 91b054fdca1ccd677c952d4ecc97b09f71e32edf Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 14 Oct 2022 07:51:30 +0200 Subject: [PATCH 008/275] Added auth service abc #70 --- src/bot_api/abc/auth_service_abc.py | 64 ++++++++++ src/bot_api/abc/select_criteria_abc.py | 17 +++ src/bot_api/filter/__init__.py | 0 .../filter/auth_user_select_criteria.py | 23 ++++ src/bot_api/model/auth_user_dto.py | 18 +++ src/bot_api/model/reset_password_dto.py | 18 +++ src/bot_api/model/settings_dto.py | 28 ++--- src/bot_api/model/token_dto.py | 18 +++ src/bot_api/model/update_auth_user_dto.py | 18 +++ src/bot_api/service/__init__.py | 1 + src/bot_api/service/auth_service.py | 4 + src/bot_data/migration/api_migration.py | 38 ++++++ src/bot_data/model/auth_role_enum.py | 7 ++ src/bot_data/model/auth_user.py | 109 ++++++++++++++++++ 14 files changed, 347 insertions(+), 16 deletions(-) create mode 100644 src/bot_api/abc/auth_service_abc.py create mode 100644 src/bot_api/abc/select_criteria_abc.py create mode 100644 src/bot_api/filter/__init__.py create mode 100644 src/bot_api/filter/auth_user_select_criteria.py create mode 100644 src/bot_api/model/auth_user_dto.py create mode 100644 src/bot_api/model/reset_password_dto.py create mode 100644 src/bot_api/model/token_dto.py create mode 100644 src/bot_api/model/update_auth_user_dto.py create mode 100644 src/bot_api/service/__init__.py create mode 100644 src/bot_api/service/auth_service.py create mode 100644 src/bot_data/migration/api_migration.py create mode 100644 src/bot_data/model/auth_role_enum.py create mode 100644 src/bot_data/model/auth_user.py diff --git a/src/bot_api/abc/auth_service_abc.py b/src/bot_api/abc/auth_service_abc.py new file mode 100644 index 00000000..0141103e --- /dev/null +++ b/src/bot_api/abc/auth_service_abc.py @@ -0,0 +1,64 @@ +from abc import ABC, abstractmethod + +from cpl_query.extension import List + +from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria +from bot_api.model.auth_user_dto import AuthUserDTO +from bot_api.model.reset_password_dto import ResetPasswordDTO +from bot_api.model.token_dto import TokenDTO +from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO +from bot_data.model.auth_user import AuthUser + + +class AuthABC(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + async def get_all_auth_users_async(self) -> List[AuthUser]: pass + + @abstractmethod + async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> List[AuthUser]: pass + + @abstractmethod + async def get_auth_user_by_email_async(self, email: str) -> AuthUser: pass + + @abstractmethod + async def find_auth_user_by_email_async(self, email: str) -> AuthUser: pass + + @abstractmethod + async def add_auth_user_async(self, user_dto: AuthUserDTO) -> AuthUser: pass + + @abstractmethod + async def confirm_email_async(self, id: str) -> AuthUser: pass + + @abstractmethod + async def login_async(self, user_dto: AuthUserDTO) -> AuthUser: pass + + @abstractmethod + async def forgot_password_async(self, email: str) -> AuthUser: pass + + @abstractmethod + async def confirm_forgot_password_async(self, id: str) -> AuthUser: pass + + @abstractmethod + async def reset_password_async(self, rp_dto: ResetPasswordDTO) -> AuthUser: pass + + @abstractmethod + async def update_user_async(self, update_user_dto: UpdateAuthUserDTO) -> AuthUser: pass + + @abstractmethod + async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO) -> AuthUser: pass + + @abstractmethod + async def refresh_async(self, token_dto: TokenDTO) -> AuthUser: pass + + @abstractmethod + async def revoke_async(self, token_dto: TokenDTO) -> AuthUser: pass + + @abstractmethod + async def delete_auth_user_by_email_async(self, email: str) -> AuthUser: pass + + @abstractmethod + async def delete_auth_user_async(self, user_dto: AuthUserDTO) -> AuthUser: pass diff --git a/src/bot_api/abc/select_criteria_abc.py b/src/bot_api/abc/select_criteria_abc.py new file mode 100644 index 00000000..234f7865 --- /dev/null +++ b/src/bot_api/abc/select_criteria_abc.py @@ -0,0 +1,17 @@ +from abc import ABC, abstractmethod + + +class SelectCriteriaABC(ABC): + + @abstractmethod + def __init__( + self, + page_index: int, + page_size: int, + sort_direction: str, + sort_column: str + ): + self.page_index = page_index + self.page_size = page_size + self.sort_direction = sort_direction + self.sort_column = sort_column diff --git a/src/bot_api/filter/__init__.py b/src/bot_api/filter/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/bot_api/filter/auth_user_select_criteria.py b/src/bot_api/filter/auth_user_select_criteria.py new file mode 100644 index 00000000..497f5eaa --- /dev/null +++ b/src/bot_api/filter/auth_user_select_criteria.py @@ -0,0 +1,23 @@ +from bot_api.abc.select_criteria_abc import SelectCriteriaABC + + +class AuthUserSelectCriteria(SelectCriteriaABC): + + def __init__( + self, + page_index: int, + page_size: int, + sort_direction: str, + sort_column: str, + + first_name: str, + last_name: str, + email: str, + auth_role=0 + ): + SelectCriteriaABC.__init__(self, page_index, page_size, sort_direction, sort_column) + + self.first_name = first_name + self.last_name = last_name + self.email = email + self.auth_role = auth_role diff --git a/src/bot_api/model/auth_user_dto.py b/src/bot_api/model/auth_user_dto.py new file mode 100644 index 00000000..f43dde4e --- /dev/null +++ b/src/bot_api/model/auth_user_dto.py @@ -0,0 +1,18 @@ +import traceback + +from cpl_core.console import Console + +from bot_api.abc.dto_abc import DtoABC + + +class AuthUserDTO(DtoABC): + + def __init__(self): + DtoABC.__init__(self) + + def from_dict(self, values: dict): + pass + + def to_dict(self) -> dict: + return { + } diff --git a/src/bot_api/model/reset_password_dto.py b/src/bot_api/model/reset_password_dto.py new file mode 100644 index 00000000..240437cd --- /dev/null +++ b/src/bot_api/model/reset_password_dto.py @@ -0,0 +1,18 @@ +import traceback + +from cpl_core.console import Console + +from bot_api.abc.dto_abc import DtoABC + + +class ResetPasswordDTO(DtoABC): + + def __init__(self): + DtoABC.__init__(self) + + def from_dict(self, values: dict): + pass + + def to_dict(self) -> dict: + return { + } diff --git a/src/bot_api/model/settings_dto.py b/src/bot_api/model/settings_dto.py index 6d72245b..ce3ec436 100644 --- a/src/bot_api/model/settings_dto.py +++ b/src/bot_api/model/settings_dto.py @@ -1,7 +1,3 @@ -import traceback - -from cpl_core.console import Console - from bot_api.abc.dto_abc import DtoABC from bot_api.model.version_dto import VersionDTO @@ -25,20 +21,20 @@ class SettingsDTO(DtoABC): ): DtoABC.__init__(self) - self._web_version = '' - self._api_version = VersionDTO() - self._config_path = '' - self._web_base_url = '' - self._api_base_url = '' + self._web_version = web_version + self._api_version = api_version + self._config_path = config_path + self._web_base_url = web_base_url + self._api_base_url = api_base_url - self._token_expire_time = 0 - self._refresh_token_expire_time = 0 + self._token_expire_time = token_expire_time + self._refresh_token_expire_time = refresh_token_expire_time - self._mail_user = '' - self._mail_port = 0 - self._mail_host = '' - self._mail_transceiver = '' - self._mail_transceiver_address = '' + self._mail_user = mail_user + self._mail_port = mail_port + self._mail_host = mail_host + self._mail_transceiver = mail_transceiver + self._mail_transceiver_address = mail_transceiver_address def from_dict(self, values: dict): self._web_version = values['WebVersion'] diff --git a/src/bot_api/model/token_dto.py b/src/bot_api/model/token_dto.py new file mode 100644 index 00000000..445943c2 --- /dev/null +++ b/src/bot_api/model/token_dto.py @@ -0,0 +1,18 @@ +import traceback + +from cpl_core.console import Console + +from bot_api.abc.dto_abc import DtoABC + + +class TokenDTO(DtoABC): + + def __init__(self): + DtoABC.__init__(self) + + def from_dict(self, values: dict): + pass + + def to_dict(self) -> dict: + return { + } diff --git a/src/bot_api/model/update_auth_user_dto.py b/src/bot_api/model/update_auth_user_dto.py new file mode 100644 index 00000000..b7d501f5 --- /dev/null +++ b/src/bot_api/model/update_auth_user_dto.py @@ -0,0 +1,18 @@ +import traceback + +from cpl_core.console import Console + +from bot_api.abc.dto_abc import DtoABC + + +class UpdateAuthUserDTO(DtoABC): + + def __init__(self): + DtoABC.__init__(self) + + def from_dict(self, values: dict): + pass + + def to_dict(self) -> dict: + return { + } diff --git a/src/bot_api/service/__init__.py b/src/bot_api/service/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/src/bot_api/service/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/src/bot_api/service/auth_service.py b/src/bot_api/service/auth_service.py new file mode 100644 index 00000000..cc9ecbbd --- /dev/null +++ b/src/bot_api/service/auth_service.py @@ -0,0 +1,4 @@ +class AuthService: + + def __init__(self): + pass diff --git a/src/bot_data/migration/api_migration.py b/src/bot_data/migration/api_migration.py new file mode 100644 index 00000000..187dc5fc --- /dev/null +++ b/src/bot_data/migration/api_migration.py @@ -0,0 +1,38 @@ +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.migration_abc import MigrationABC +from bot_data.db_context import DBContext + + +class ApiMigration(MigrationABC): + name = '0.3_ApiMigration' + + def __init__(self, logger: DatabaseLogger, db: DBContext): + MigrationABC.__init__(self) + self._logger = logger + self._db = db + self._cursor = db.cursor + + def upgrade(self): + self._logger.debug(__name__, 'Running upgrade') + + self._cursor.execute( + str(f""" + CREATE TABLE IF NOT EXISTS `AuthUsers` ( + `Id` bigint NOT NULL, + `FirstName` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, + `LastName` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, + `EMail` varchar(255) DEFAULT NULL, + `Password` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, + `RefreshToken` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, + `ConfirmationId` varchar(255) DEFAULT NULL, + `ForgotPasswordId` varchar(255) DEFAULT NULL, + `RefreshTokenExpiryTime` datetime(6) NOT NULL, + `AuthRole` int NOT NULL DEFAULT '0' + `CreatedOn` datetime(6) NOT NULL, + `LastModifiedOn` datetime(6) NOT NULL, + ) + """) + ) + + def downgrade(self): + self._cursor.execute('DROP TABLE `AuthUsers`;') diff --git a/src/bot_data/model/auth_role_enum.py b/src/bot_data/model/auth_role_enum.py new file mode 100644 index 00000000..8b032325 --- /dev/null +++ b/src/bot_data/model/auth_role_enum.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class AuthRoleEnum(Enum): + + Normal = 0 + Admin = 1 diff --git a/src/bot_data/model/auth_user.py b/src/bot_data/model/auth_user.py new file mode 100644 index 00000000..37b50d99 --- /dev/null +++ b/src/bot_data/model/auth_user.py @@ -0,0 +1,109 @@ +from datetime import datetime +from typing import Optional +from cpl_core.database import TableABC + +from bot_data.model.auth_role_enum import AuthRoleEnum +from bot_data.model.server import Server + + +class AuthUser(TableABC): + + def __init__( + self, + first_name: str, + last_name: str, + email: str, + password: str, + refresh_token: str, + confirmation_id: str, + forgot_password_id: str, + refresh_token_expire_time: datetime, + auth_role: AuthRoleEnum, + created_at: datetime = None, + modified_at: datetime = None, + id=0 + ): + self._auth_user_id = id + self._first_name = first_name + self._last_name = last_name + self._email = email + self._password = password + self._refresh_token = refresh_token + self._confirmation_id = confirmation_id + self._forgot_password_id = forgot_password_id + self._refresh_token_expire_time = refresh_token_expire_time + + self._auth_role_id = auth_role.value + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @staticmethod + def get_select_all_string() -> str: + return str(f""" + SELECT * FROM `AuthUsers`; + """) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `AuthUsers` + WHERE `Id` = {id}; + """) + + @property + def insert_string(self) -> str: + return str(f""" + INSERT INTO `AuthUsers` ( + `Id`, + `FirstName`, + `LastName`, + `EMail`, + `Password`, + `RefreshToken`, + `ConfirmationId`, + `ForgotPasswordId`, + `RefreshTokenExpiryTime`, + `AuthRole`, + `CreatedOn`, + `LastModifiedOn` + ) VALUES ( + {self._auth_user_id}, + {self._first_name}, + {self._last_name}, + {self._email}, + {self._password}, + {self._refresh_token}, + {self._confirmation_id}, + {self._forgot_password_id}, + {self._refresh_token_expire_time}, + {self._auth_role_id} + {self._created_at}, + {self._modified_at} + ) + """) + + @property + def udpate_string(self) -> str: + return str(f""" + UPDATE `AuthUsers` + SET `FirstName` = '{self._first_name}', + `LastName` = '{self._last_name}', + `EMail` = '{self._email}', + `Password` = '{self._password}', + `RefreshToken` = '{self._refresh_token}', + `ConfirmationId` = '{self._confirmation_id}', + `ForgotPasswordId` = '{self._forgot_password_id}', + `RefreshTokenExpiryTime` = '{self._refresh_token_expire_time}', + `AutoRole` = {self._auth_role_id}, + `LastModifiedAt` = '{self._modified_at}' + WHERE `AuthUsers`.`Id` = {self._auth_user_id}; + """) + + @property + def delete_string(self) -> str: + return str(f""" + DELETE FROM `AuthUsers` + WHERE `Id` = {self._auth_user_id}; + """) From 411e44a6812086a27e4fad3e634cc138c249857d Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 14 Oct 2022 10:35:58 +0200 Subject: [PATCH 009/275] [WIP] Added auth controller & updated pakages #70 --- src/bot_api/api.py | 18 ++++++--- src/bot_api/api_module.py | 2 + src/bot_api/bot-api.json | 7 +++- src/bot_api/configuration/__init__.py | 25 ++++++++++++ src/bot_api/controller/auth_controller.py | 47 +++++++++++++++++++++++ src/bot_api/filter/__init__.py | 26 +++++++++++++ src/bot_api/route/route.py | 4 +- src/bot_api/service/__init__.py | 25 ++++++++++++ src/bot_core/bot-core.json | 2 +- src/bot_data/bot-data.json | 2 +- src/modules/admin/admin.json | 2 +- src/modules/auto_role/auto-role.json | 2 +- src/modules/base/base.json | 2 +- src/modules/boot_log/boot-log.json | 2 +- src/modules/database/database.json | 2 +- src/modules/moderator/moderator.json | 2 +- src/modules/permission/permission.json | 2 +- 17 files changed, 154 insertions(+), 18 deletions(-) create mode 100644 src/bot_api/controller/auth_controller.py diff --git a/src/bot_api/api.py b/src/bot_api/api.py index c4043505..29c176fa 100644 --- a/src/bot_api/api.py +++ b/src/bot_api/api.py @@ -1,8 +1,10 @@ import sys from functools import partial +from blinker import Namespace from cpl_core.dependency_injection import ServiceProviderABC from flask import Flask, request +from flask_cors import CORS from bot_api.configuration.api_settings import ApiSettings from bot_api.logging.api_logger import ApiLogger @@ -27,20 +29,26 @@ class Api(Flask): self._services = services self._apt_settings = api_settings + self._cors = CORS(self, support_credentials=True) + # register before request self.before_request_funcs.setdefault(None, []).append(self.before_request) + my_signals = Namespace() + notify = my_signals.signal('notify') def _register_routes(self): for path, f in Route.registered_routes.items(): + route = f[0] + kwargs = f[1] cls = None - qual_name_split = f.__qualname__.split('.') + qual_name_split = route.__qualname__.split('.') if len(qual_name_split) > 0: - cls_type = vars(sys.modules[f.__module__])[qual_name_split[0]] + cls_type = vars(sys.modules[route.__module__])[qual_name_split[0]] cls = self._services.get_service(cls_type) - partial_f = partial(f, self if cls is None else cls) - partial_f.__name__ = f.__name__ - self.route(path)(partial_f) + partial_f = partial(route, self if cls is None else cls) + partial_f.__name__ = route.__name__ + self.route(path, **kwargs)(partial_f) def before_request(self, *args, **kwargs): self._logger.debug(__name__, f'Received GET @{request.url}') diff --git a/src/bot_api/api_module.py b/src/bot_api/api_module.py index 3f26d80c..be126e65 100644 --- a/src/bot_api/api_module.py +++ b/src/bot_api/api_module.py @@ -10,6 +10,7 @@ from flask import Flask from bot_api.api import Api from bot_api.api_thread import ApiThread from bot_api.controller.api_controller import ApiController +from bot_api.controller.auth_controller import AuthController from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum @@ -33,4 +34,5 @@ class ApiModule(ModuleABC): services.add_singleton(ApiThread) services.add_singleton(Flask, Api) + services.add_transient(AuthController) services.add_transient(ApiController) diff --git a/src/bot_api/bot-api.json b/src/bot_api/bot-api.json index 09e41293..7b66f8ef 100644 --- a/src/bot_api/bot-api.json +++ b/src/bot_api/bot-api.json @@ -17,11 +17,14 @@ "LicenseDescription": "", "Dependencies": [ "cpl-core==2022.10.0.post6", + "Flask==2.2.2", "Flask[async]==2.2.2", - "Flask-Classful==0.14.2" + "Flask-Classful==0.14.2", + "Flask-Cors==3.0.10", + "PyJWT==2.5.0" ], "DevDependencies": [ - "cpl-cli>=2022.10.0" + "cpl-cli==2022.10.0" ], "PythonVersion": ">=3.10.4", "PythonPath": { diff --git a/src/bot_api/configuration/__init__.py b/src/bot_api/configuration/__init__.py index 425ab6c1..86e07283 100644 --- a/src/bot_api/configuration/__init__.py +++ b/src/bot_api/configuration/__init__.py @@ -1 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'bot_api.configuration' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.2.3' + +from collections import namedtuple + + # imports + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='2', micro='3') diff --git a/src/bot_api/controller/auth_controller.py b/src/bot_api/controller/auth_controller.py new file mode 100644 index 00000000..40427bda --- /dev/null +++ b/src/bot_api/controller/auth_controller.py @@ -0,0 +1,47 @@ +from cpl_core.configuration import ConfigurationABC +from cpl_core.environment import ApplicationEnvironmentABC +from cpl_core.mailing import EMailClientABC, EMailClientSettings +from cpl_query.extension import List +from cpl_translation import TranslatePipe +from flask import request +from flask_cors import cross_origin + +from bot_api.api import Api +from bot_api.logging.api_logger import ApiLogger +from bot_api.model.token_dto import TokenDTO +from bot_api.route.route import Route +from bot_data.model.auth_user import AuthUser + + +class AuthController: + + def __init__( + self, + config: ConfigurationABC, + env: ApplicationEnvironmentABC, + logger: ApiLogger, + t: TranslatePipe, + api: Api, + mail_settings: EMailClientSettings, + mailer: EMailClientABC + ): + self._config = config + self._env = env + self._logger = logger + self._t = t + self._api = api + self._mail_settings = mail_settings + self._mailer = mailer + + @Route.route('/api/auth/get-all-users') + async def get_all_users(self) -> List[AuthUser]: + pass + + @Route.route('/api/auth/get-filtered-users', methods=['POST']) + async def get_filtered_users(self) -> List[AuthUser]: + pass + + @Route.route('/api/auth/login', methods=['POST']) + async def login(self) -> dict: + self._logger.warn(__name__, f'Received {request.json}') + return TokenDTO().to_dict() diff --git a/src/bot_api/filter/__init__.py b/src/bot_api/filter/__init__.py index e69de29b..0f9e52aa 100644 --- a/src/bot_api/filter/__init__.py +++ b/src/bot_api/filter/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'bot_api.filter' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.2.3' + +from collections import namedtuple + + +# imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='2', micro='3') diff --git a/src/bot_api/route/route.py b/src/bot_api/route/route.py index ef0e3994..9dbe7fcb 100644 --- a/src/bot_api/route/route.py +++ b/src/bot_api/route/route.py @@ -2,10 +2,10 @@ class Route: registered_routes = {} @classmethod - def route(cls, path=None): + def route(cls, path=None, **kwargs): # simple decorator for class based views def inner(fn): - cls.registered_routes[path] = fn + cls.registered_routes[path] = (fn, kwargs) return fn return inner diff --git a/src/bot_api/service/__init__.py b/src/bot_api/service/__init__.py index 425ab6c1..3a5cf24d 100644 --- a/src/bot_api/service/__init__.py +++ b/src/bot_api/service/__init__.py @@ -1 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'bot_api.service' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.2.3' + +from collections import namedtuple + + # imports + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='2', micro='3') diff --git a/src/bot_core/bot-core.json b/src/bot_core/bot-core.json index 6720bf0f..502ce6c6 100644 --- a/src/bot_core/bot-core.json +++ b/src/bot_core/bot-core.json @@ -19,7 +19,7 @@ "cpl-core>=2022.10.0" ], "DevDependencies": [ - "cpl-cli>=2022.10.0" + "cpl-cli==2022.10.0" ], "PythonVersion": ">=3.10.4", "PythonPath": { diff --git a/src/bot_data/bot-data.json b/src/bot_data/bot-data.json index e92af7de..99af9f44 100644 --- a/src/bot_data/bot-data.json +++ b/src/bot_data/bot-data.json @@ -19,7 +19,7 @@ "cpl-core>=2022.10.0" ], "DevDependencies": [ - "cpl-cli>=2022.10.0" + "cpl-cli==2022.10.0" ], "PythonVersion": ">=3.10.4", "PythonPath": { diff --git a/src/modules/admin/admin.json b/src/modules/admin/admin.json index 446e16f3..e76e9180 100644 --- a/src/modules/admin/admin.json +++ b/src/modules/admin/admin.json @@ -19,7 +19,7 @@ "cpl-core>=2022.10.0.post5" ], "DevDependencies": [ - "cpl-cli>=2022.10.0" + "cpl-cli==2022.10.0" ], "PythonVersion": ">=3.10.4", "PythonPath": { diff --git a/src/modules/auto_role/auto-role.json b/src/modules/auto_role/auto-role.json index 8e29094c..f8756155 100644 --- a/src/modules/auto_role/auto-role.json +++ b/src/modules/auto_role/auto-role.json @@ -19,7 +19,7 @@ "cpl-core>=2022.10.0.post5" ], "DevDependencies": [ - "cpl-cli>=2022.10.0" + "cpl-cli==2022.10.0" ], "PythonVersion": ">=3.10.4", "PythonPath": { diff --git a/src/modules/base/base.json b/src/modules/base/base.json index 4371622c..a77d7c5b 100644 --- a/src/modules/base/base.json +++ b/src/modules/base/base.json @@ -19,7 +19,7 @@ "cpl-core>=2022.10.0.post2" ], "DevDependencies": [ - "cpl-cli>=2022.10.0" + "cpl-cli==2022.10.0" ], "PythonVersion": ">=3.10.4", "PythonPath": { diff --git a/src/modules/boot_log/boot-log.json b/src/modules/boot_log/boot-log.json index bf1e0b5b..0090d83c 100644 --- a/src/modules/boot_log/boot-log.json +++ b/src/modules/boot_log/boot-log.json @@ -19,7 +19,7 @@ "cpl-core>=2022.10.0.post2" ], "DevDependencies": [ - "cpl-cli>=2022.10.0" + "cpl-cli==2022.10.0" ], "PythonVersion": ">=3.10.4", "PythonPath": { diff --git a/src/modules/database/database.json b/src/modules/database/database.json index efcaea3f..c4b05683 100644 --- a/src/modules/database/database.json +++ b/src/modules/database/database.json @@ -19,7 +19,7 @@ "cpl-core>=2022.10.0.post2" ], "DevDependencies": [ - "cpl-cli>=2022.10.0" + "cpl-cli==2022.10.0" ], "PythonVersion": ">=3.10.4", "PythonPath": { diff --git a/src/modules/moderator/moderator.json b/src/modules/moderator/moderator.json index 45dca24c..034735a6 100644 --- a/src/modules/moderator/moderator.json +++ b/src/modules/moderator/moderator.json @@ -19,7 +19,7 @@ "cpl-core>=2022.10.0.post5" ], "DevDependencies": [ - "cpl-cli>=2022.10.0" + "cpl-cli==2022.10.0" ], "PythonVersion": ">=3.10.4", "PythonPath": { diff --git a/src/modules/permission/permission.json b/src/modules/permission/permission.json index f5c96087..86cbc3c7 100644 --- a/src/modules/permission/permission.json +++ b/src/modules/permission/permission.json @@ -19,7 +19,7 @@ "cpl-core>=2022.10.0.post2" ], "DevDependencies": [ - "cpl-cli>=2022.10.0" + "cpl-cli==2022.10.0" ], "PythonVersion": ">=3.10.4", "PythonPath": { From 2457107c63d0110f24e2ce44a4ddb48f4c1255cc Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 14 Oct 2022 13:05:53 +0200 Subject: [PATCH 010/275] Added auth user repository #70 --- src/bot_data/abc/auth_user_repository_abc.py | 41 ++++++ src/bot_data/data_module.py | 3 + src/bot_data/filtered_result.py | 24 ++++ src/bot_data/model/auth_user.py | 59 ++++++++- .../service/auth_user_repository_service.py | 125 ++++++++++++++++++ 5 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 src/bot_data/abc/auth_user_repository_abc.py create mode 100644 src/bot_data/filtered_result.py create mode 100644 src/bot_data/service/auth_user_repository_service.py diff --git a/src/bot_data/abc/auth_user_repository_abc.py b/src/bot_data/abc/auth_user_repository_abc.py new file mode 100644 index 00000000..16118746 --- /dev/null +++ b/src/bot_data/abc/auth_user_repository_abc.py @@ -0,0 +1,41 @@ +from abc import ABC, abstractmethod +from typing import Optional + +from cpl_query.extension import List + +from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria +from bot_data.filtered_result import FilteredResult +from bot_data.model.auth_user import AuthUser + + +class AuthUserRepositoryABC(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + def get_all_auth_users(self) -> List[AuthUser]: pass + + @abstractmethod + def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> FilteredResult: pass + + @abstractmethod + def get_auth_user_by_email_async(self, email: str) -> AuthUser: pass + + @abstractmethod + def find_auth_user_by_email_async(self, email: str) -> Optional[AuthUser]: pass + + @abstractmethod + def find_auth_user_by_confirmation_id_async(self, id: str) -> Optional[AuthUser]: pass + + @abstractmethod + def find_auth_user_by_forgot_password_id_async(self, id: str) -> Optional[AuthUser]: pass + + @abstractmethod + def add_auth_user(self, user: AuthUser): pass + + @abstractmethod + def update_auth_user(self, user: AuthUser): pass + + @abstractmethod + def delete_auth_user(self, user: AuthUser): pass diff --git a/src/bot_data/data_module.py b/src/bot_data/data_module.py index 99fc12ad..7537b6f2 100644 --- a/src/bot_data/data_module.py +++ b/src/bot_data/data_module.py @@ -5,6 +5,7 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.client_repository_abc import ClientRepositoryABC from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC @@ -12,6 +13,7 @@ from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.service.auth_user_repository_service import AuthUserRepositoryService from bot_data.service.auto_role_repository_service import AutoRoleRepositoryService from bot_data.service.client_repository_service import ClientRepositoryService from bot_data.service.known_user_repository_service import KnownUserRepositoryService @@ -30,6 +32,7 @@ class DataModule(ModuleABC): pass def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): + services.add_transient(AuthUserRepositoryABC, AuthUserRepositoryService) services.add_transient(ServerRepositoryABC, ServerRepositoryService) services.add_transient(UserRepositoryABC, UserRepositoryService) services.add_transient(ClientRepositoryABC, ClientRepositoryService) diff --git a/src/bot_data/filtered_result.py b/src/bot_data/filtered_result.py new file mode 100644 index 00000000..8bdf90a3 --- /dev/null +++ b/src/bot_data/filtered_result.py @@ -0,0 +1,24 @@ +from cpl_query.extension import List + + +class FilteredResult: + + def __init__(self, result: List = None, total_count: int = 0): + self._result = [] if result is None else result + self._total_count = total_count + + @property + def result(self) -> List: + return self._result + + @result.setter + def result(self, value: List): + self._result = value + + @property + def total_count(self) -> int: + return self._total_count + + @total_count.setter + def total_count(self, value: int): + self._total_count = value diff --git a/src/bot_data/model/auth_user.py b/src/bot_data/model/auth_user.py index 37b50d99..6078f209 100644 --- a/src/bot_data/model/auth_user.py +++ b/src/bot_data/model/auth_user.py @@ -33,12 +33,44 @@ class AuthUser(TableABC): self._forgot_password_id = forgot_password_id self._refresh_token_expire_time = refresh_token_expire_time - self._auth_role_id = auth_role.value + self._auth_role_id = auth_role TableABC.__init__(self) self._created_at = created_at if created_at is not None else self._created_at self._modified_at = modified_at if modified_at is not None else self._modified_at + @property + def id(self) -> int: + return self._auth_user_id + + @property + def first_name(self) -> str: + return self._first_name + + @property + def last_name(self) -> str: + return self._last_name + + @property + def email(self) -> str: + return self._email + + @property + def password(self) -> str: + return self._password + + @property + def refresh_token(self) -> str: + return self._refresh_token + + @property + def refresh_token_expire_time(self) -> datetime: + return self._refresh_token_expire_time + + @property + def auth_role(self) -> AuthRoleEnum: + return self._auth_role_id + @staticmethod def get_select_all_string() -> str: return str(f""" @@ -52,6 +84,27 @@ class AuthUser(TableABC): WHERE `Id` = {id}; """) + @staticmethod + def get_select_by_email_string(email: str) -> str: + return str(f""" + SELECT * FROM `AuthUsers` + WHERE `EMail` = {email}; + """) + + @staticmethod + def get_select_by_confirmation_id_string(id: str) -> str: + return str(f""" + SELECT * FROM `AuthUsers` + WHERE `ConfirmationId` = {id}; + """) + + @staticmethod + def get_select_by_forgot_password_i_string(id: str) -> str: + return str(f""" + SELECT * FROM `AuthUsers` + WHERE `ForgotPasswordId` = {id}; + """) + @property def insert_string(self) -> str: return str(f""" @@ -78,7 +131,7 @@ class AuthUser(TableABC): {self._confirmation_id}, {self._forgot_password_id}, {self._refresh_token_expire_time}, - {self._auth_role_id} + {self._auth_role_id.value} {self._created_at}, {self._modified_at} ) @@ -96,7 +149,7 @@ class AuthUser(TableABC): `ConfirmationId` = '{self._confirmation_id}', `ForgotPasswordId` = '{self._forgot_password_id}', `RefreshTokenExpiryTime` = '{self._refresh_token_expire_time}', - `AutoRole` = {self._auth_role_id}, + `AutoRole` = {self._auth_role_id.value}, `LastModifiedAt` = '{self._modified_at}' WHERE `AuthUsers`.`Id` = {self._auth_user_id}; """) diff --git a/src/bot_data/service/auth_user_repository_service.py b/src/bot_data/service/auth_user_repository_service.py new file mode 100644 index 00000000..aca3e94a --- /dev/null +++ b/src/bot_data/service/auth_user_repository_service.py @@ -0,0 +1,125 @@ +from typing import Optional + +from cpl_core.database.context import DatabaseContextABC +from cpl_query.extension import List + +from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC +from bot_data.filtered_result import FilteredResult +from bot_data.model.auth_role_enum import AuthRoleEnum +from bot_data.model.auth_user import AuthUser + + +class AuthUserRepositoryService(AuthUserRepositoryABC): + + def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC): + self._logger = logger + self._context = db_context + + AuthUserRepositoryABC.__init__(self) + + @staticmethod + def _user_from_result(result: tuple) -> AuthUser: + return AuthUser( + result[1], + result[2], + result[3], + result[4], + result[5], + result[6], + result[7], + result[8], + AuthRoleEnum(result[9]), + id=result[0] + ) + + def get_all_auth_users(self) -> List[AuthUser]: + users = List(AuthUser) + self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_all_string()}') + results = self._context.select(AuthUser.get_select_all_string()) + for result in results: + self._logger.trace(__name__, f'Get auth user with id {result[0]}') + users.append(self._user_from_result(result)) + + return users + + def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> FilteredResult: + users = self.get_all_auth_users() + self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_all_string()}') + + query = users + + if criteria.first_name is not None and criteria.first_name != '': + query = query.where(lambda x: criteria.first_name in x.first_name or x.first_name == criteria.first_name) + + if criteria.last_name is not None and criteria.last_name != '': + query = query.where(lambda x: criteria.last_name in x.last_name or x.last_name == criteria.last_name) + + if criteria.email is not None and criteria.email != '': + query = query.where(lambda x: criteria.email in x.email or x.email == criteria.email) + + if criteria.auth_role is not None: + query = query.where(lambda x: x.auth_role == AuthRoleEnum(criteria.auth_role)) + + # sort + if criteria.sort_column is not None and criteria.sort_column != '' and criteria.sort_direction is not None and criteria.sort_direction: + crit_sort_direction = criteria.sort_direction.lower() + if crit_sort_direction == "desc" or crit_sort_direction == "descending": + query = query.order_by_descending(lambda x: getattr(x, criteria.sort_column)) + else: + query = query.order_by(lambda x: getattr(x, criteria.sort_column)) + + skip = criteria.page_size * criteria.page_index + result = FilteredResult() + result.total_count = query.count() + result.result = query.skip(skip).take(criteria.page_size) + + return result + + def get_auth_user_by_email_async(self, email: str) -> AuthUser: + self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_by_email_string(email)}') + result = self._context.select(AuthUser.get_select_by_email_string(email))[0] + return self._user_from_result(result) + + def find_auth_user_by_email_async(self, email: str) -> Optional[AuthUser]: + self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_by_email_string(email)}') + result = self._context.select(AuthUser.get_select_by_email_string(email)) + if result is None or len(result) == 0: + return None + + result = result[0] + + return self._user_from_result(result) + + def find_auth_user_by_confirmation_id_async(self, id: str) -> Optional[AuthUser]: + self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_by_email_string(id)}') + result = self._context.select(AuthUser.get_select_by_email_string(id)) + if result is None or len(result) == 0: + return None + + result = result[0] + + return self._user_from_result(result) + + def find_auth_user_by_forgot_password_id_async(self, id: str) -> Optional[AuthUser]: + self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_by_email_string(id)}') + result = self._context.select(AuthUser.get_select_by_email_string(id)) + if result is None or len(result) == 0: + return None + + result = result[0] + + return self._user_from_result(result) + + def add_auth_user(self, user: AuthUser): + self._logger.trace(__name__, f'Send SQL command: {user.insert_string}') + self._context.cursor.execute(user.insert_string) + + def update_auth_user(self, user: AuthUser): + self._logger.trace(__name__, f'Send SQL command: {user.udpate_string}') + self._context.cursor.execute(user.udpate_string) + + def delete_auth_user(self, user: AuthUser): + self._logger.trace(__name__, f'Send SQL command: {user.delete_string}') + self._context.cursor.execute(user.delete_string) From 126637306def102558fbcefa13ec5098f372b863 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 14 Oct 2022 13:17:25 +0200 Subject: [PATCH 011/275] [WIP] Added auth user service #70 --- src/bot_api/abc/auth_service_abc.py | 2 +- src/bot_api/model/auth_user_dto.py | 46 +++++++++++++++++++++- src/bot_api/service/auth_service.py | 61 ++++++++++++++++++++++++++++- 3 files changed, 105 insertions(+), 4 deletions(-) diff --git a/src/bot_api/abc/auth_service_abc.py b/src/bot_api/abc/auth_service_abc.py index 0141103e..c34af369 100644 --- a/src/bot_api/abc/auth_service_abc.py +++ b/src/bot_api/abc/auth_service_abc.py @@ -10,7 +10,7 @@ from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO from bot_data.model.auth_user import AuthUser -class AuthABC(ABC): +class AuthServiceABC(ABC): @abstractmethod def __init__(self): pass diff --git a/src/bot_api/model/auth_user_dto.py b/src/bot_api/model/auth_user_dto.py index f43dde4e..7ca44a9e 100644 --- a/src/bot_api/model/auth_user_dto.py +++ b/src/bot_api/model/auth_user_dto.py @@ -1,18 +1,60 @@ import traceback +from typing import Optional from cpl_core.console import Console from bot_api.abc.dto_abc import DtoABC +from bot_data.model.auth_role_enum import AuthRoleEnum class AuthUserDTO(DtoABC): - def __init__(self): + def __init__( + self, + id: int, + first_name: str, + last_name: str, + email: str, + password: str, + confirmation_id: Optional[str], + auth_role: AuthRoleEnum, + ): DtoABC.__init__(self) + self._id = id + self._first_name = first_name + self._last_name = last_name + self._email = email + self._password = password + self._is_confirmed = confirmation_id is None + self._auth_role = auth_role + + """ + long Id { get; set; } + string FirstName { get; set; } + string LastName { get; set; } + string EMail { get; set; } + string Password { get; set; } + bool IsConfirmed { get; set; } + AuthRoles AuthRole { get; set; } + """ + def from_dict(self, values: dict): - pass + self._id = values['Id'] + self._first_name = values['FirstName'] + self._last_name = values['LastName'] + self._email = values['EMail'] + self._password = values['Password'] + self._is_confirmed = values['IsConfirmed'] + self._auth_role = values['AuthRole'] def to_dict(self) -> dict: return { + "Id": self._id, + "FirstName": self._first_name, + "LastName": self._last_name, + "EMail": self._email, + "Password": self._password, + "IsConfirmed": self._is_confirmed, + "AuthRole": self._auth_role, } diff --git a/src/bot_api/service/auth_service.py b/src/bot_api/service/auth_service.py index cc9ecbbd..3455c083 100644 --- a/src/bot_api/service/auth_service.py +++ b/src/bot_api/service/auth_service.py @@ -1,4 +1,63 @@ -class AuthService: +from cpl_query.extension import List + +from bot_api.abc.auth_service_abc import AuthServiceABC +from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria +from bot_api.model.auth_user_dto import AuthUserDTO +from bot_api.model.reset_password_dto import ResetPasswordDTO +from bot_api.model.token_dto import TokenDTO +from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO +from bot_data.model.auth_user import AuthUser + + +class AuthService(AuthServiceABC): def __init__(self): pass + + async def get_all_auth_users_async(self) -> List[AuthUser]: + pass + + async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> List[AuthUser]: + pass + + async def get_auth_user_by_email_async(self, email: str) -> AuthUser: + pass + + async def find_auth_user_by_email_async(self, email: str) -> AuthUser: + pass + + async def add_auth_user_async(self, user_dto: AuthUserDTO) -> AuthUser: + pass + + async def confirm_email_async(self, id: str) -> AuthUser: + pass + + async def login_async(self, user_dto: AuthUserDTO) -> AuthUser: + pass + + async def forgot_password_async(self, email: str) -> AuthUser: + pass + + async def confirm_forgot_password_async(self, id: str) -> AuthUser: + pass + + async def reset_password_async(self, rp_dto: ResetPasswordDTO) -> AuthUser: + pass + + async def update_user_async(self, update_user_dto: UpdateAuthUserDTO) -> AuthUser: + pass + + async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO) -> AuthUser: + pass + + async def refresh_async(self, token_dto: TokenDTO) -> AuthUser: + pass + + async def revoke_async(self, token_dto: TokenDTO) -> AuthUser: + pass + + async def delete_auth_user_by_email_async(self, email: str) -> AuthUser: + pass + + async def delete_auth_user_async(self, user_dto: AuthUserDTO) -> AuthUser: + pass From 1defe29406fa5a9ebda3db20a15057237ea2c422 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 14 Oct 2022 15:37:52 +0200 Subject: [PATCH 012/275] Added auth user controller #70 --- src/bot_api/abc/auth_service_abc.py | 28 +++--- src/bot_api/api.py | 1 - src/bot_api/api_module.py | 3 + src/bot_api/controller/auth_controller.py | 97 ++++++++++++++++--- src/bot_api/json_processor.py | 32 ++++++ src/bot_api/model/auth_user_dto.py | 42 +++++--- .../model/auth_user_filtered_result_dto.py | 21 ++++ src/bot_api/model/email_string_dto.py | 18 ++++ src/bot_api/route/route.py | 20 ++++ src/bot_api/service/auth_service.py | 15 ++- 10 files changed, 232 insertions(+), 45 deletions(-) create mode 100644 src/bot_api/json_processor.py create mode 100644 src/bot_api/model/auth_user_filtered_result_dto.py create mode 100644 src/bot_api/model/email_string_dto.py diff --git a/src/bot_api/abc/auth_service_abc.py b/src/bot_api/abc/auth_service_abc.py index c34af369..45788c9a 100644 --- a/src/bot_api/abc/auth_service_abc.py +++ b/src/bot_api/abc/auth_service_abc.py @@ -4,6 +4,8 @@ from cpl_query.extension import List from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria from bot_api.model.auth_user_dto import AuthUserDTO +from bot_api.model.auth_user_filtered_result_dto import AuthUserFilteredResultDTO +from bot_api.model.email_string_dto import EMailStringDTO from bot_api.model.reset_password_dto import ResetPasswordDTO from bot_api.model.token_dto import TokenDTO from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO @@ -19,7 +21,7 @@ class AuthServiceABC(ABC): async def get_all_auth_users_async(self) -> List[AuthUser]: pass @abstractmethod - async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> List[AuthUser]: pass + async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO: pass @abstractmethod async def get_auth_user_by_email_async(self, email: str) -> AuthUser: pass @@ -28,37 +30,37 @@ class AuthServiceABC(ABC): async def find_auth_user_by_email_async(self, email: str) -> AuthUser: pass @abstractmethod - async def add_auth_user_async(self, user_dto: AuthUserDTO) -> AuthUser: pass + async def add_auth_user_async(self, user_dto: AuthUserDTO) -> int: pass @abstractmethod - async def confirm_email_async(self, id: str) -> AuthUser: pass + async def confirm_email_async(self, id: str) -> bool: pass @abstractmethod - async def login_async(self, user_dto: AuthUserDTO) -> AuthUser: pass + async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: pass @abstractmethod - async def forgot_password_async(self, email: str) -> AuthUser: pass + async def forgot_password_async(self, email: str): pass @abstractmethod - async def confirm_forgot_password_async(self, id: str) -> AuthUser: pass + async def confirm_forgot_password_async(self, id: str) -> EMailStringDTO: pass @abstractmethod - async def reset_password_async(self, rp_dto: ResetPasswordDTO) -> AuthUser: pass + async def reset_password_async(self, rp_dto: ResetPasswordDTO): pass @abstractmethod - async def update_user_async(self, update_user_dto: UpdateAuthUserDTO) -> AuthUser: pass + async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): pass @abstractmethod - async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO) -> AuthUser: pass + async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO): pass @abstractmethod - async def refresh_async(self, token_dto: TokenDTO) -> AuthUser: pass + async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO: pass @abstractmethod - async def revoke_async(self, token_dto: TokenDTO) -> AuthUser: pass + async def revoke_async(self, token_dto: TokenDTO): pass @abstractmethod - async def delete_auth_user_by_email_async(self, email: str) -> AuthUser: pass + async def delete_auth_user_by_email_async(self, email: str): pass @abstractmethod - async def delete_auth_user_async(self, user_dto: AuthUserDTO) -> AuthUser: pass + async def delete_auth_user_async(self, user_dto: AuthUserDTO): pass diff --git a/src/bot_api/api.py b/src/bot_api/api.py index 29c176fa..8760377b 100644 --- a/src/bot_api/api.py +++ b/src/bot_api/api.py @@ -54,7 +54,6 @@ class Api(Flask): self._logger.debug(__name__, f'Received GET @{request.url}') headers = str(request.headers).replace("\n", "\n\t") self._logger.trace(__name__, f'Headers: \n\t{headers}') - self._logger.trace(__name__, f'Body: {request.get_json(force=True, silent=True)}') def start(self): self._logger.info(__name__, f'Starting API {self._apt_settings.host}:{self._apt_settings.port}') diff --git a/src/bot_api/api_module.py b/src/bot_api/api_module.py index be126e65..ff1be35b 100644 --- a/src/bot_api/api_module.py +++ b/src/bot_api/api_module.py @@ -7,10 +7,12 @@ from cpl_core.mailing import EMailClientABC, EMailClient from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from flask import Flask +from bot_api.abc.auth_service_abc import AuthServiceABC from bot_api.api import Api from bot_api.api_thread import ApiThread from bot_api.controller.api_controller import ApiController from bot_api.controller.auth_controller import AuthController +from bot_api.service.auth_service import AuthService from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum @@ -34,5 +36,6 @@ class ApiModule(ModuleABC): services.add_singleton(ApiThread) services.add_singleton(Flask, Api) + services.add_transient(AuthServiceABC, AuthService) services.add_transient(AuthController) services.add_transient(ApiController) diff --git a/src/bot_api/controller/auth_controller.py b/src/bot_api/controller/auth_controller.py index 40427bda..adb6f3a2 100644 --- a/src/bot_api/controller/auth_controller.py +++ b/src/bot_api/controller/auth_controller.py @@ -1,19 +1,22 @@ from cpl_core.configuration import ConfigurationABC from cpl_core.environment import ApplicationEnvironmentABC from cpl_core.mailing import EMailClientABC, EMailClientSettings -from cpl_query.extension import List from cpl_translation import TranslatePipe -from flask import request -from flask_cors import cross_origin +from flask import request, jsonify, Response +from bot_api.abc.auth_service_abc import AuthServiceABC from bot_api.api import Api +from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria +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.token_dto import TokenDTO +from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO from bot_api.route.route import Route -from bot_data.model.auth_user import AuthUser class AuthController: + BasePath = '/api/auth' def __init__( self, @@ -23,7 +26,8 @@ class AuthController: t: TranslatePipe, api: Api, mail_settings: EMailClientSettings, - mailer: EMailClientABC + mailer: EMailClientABC, + auth_service: AuthServiceABC ): self._config = config self._env = env @@ -32,16 +36,79 @@ class AuthController: self._api = api self._mail_settings = mail_settings self._mailer = mailer + self._auth_service = auth_service - @Route.route('/api/auth/get-all-users') - async def get_all_users(self) -> List[AuthUser]: - pass + @Route.get(f'{BasePath}/users') + async def get_all_users(self) -> Response: + return jsonify(await self._auth_service.get_all_auth_users_async()) - @Route.route('/api/auth/get-filtered-users', methods=['POST']) - async def get_filtered_users(self) -> List[AuthUser]: - pass + @Route.post(f'{BasePath}/users/get/filtered') + async def get_filtered_users(self) -> Response: + dto: AuthUserSelectCriteria = JSONProcessor.process(AuthUserSelectCriteria, request.get_json(force=True, silent=True)) + result = await self._auth_service.get_filtered_auth_users_async(dto) + return jsonify(result.to_dict()) - @Route.route('/api/auth/login', methods=['POST']) - async def login(self) -> dict: - self._logger.warn(__name__, f'Received {request.json}') - return TokenDTO().to_dict() + @Route.get(f'{BasePath}/users/get/') + async def get_user_from_email(self, email: str) -> Response: + return jsonify(await self._auth_service.get_auth_user_by_email_async(email)) + + @Route.get(f'{BasePath}/users/find/') + async def find_user_from_email(self, email: str) -> Response: + return jsonify(await self._auth_service.find_auth_user_by_email_async(email)) + + @Route.post(f'{BasePath}/register') + async def register(self): + dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True)) + await self._auth_service.add_auth_user_async(dto) + return '', 200 + + @Route.post(f'{BasePath}/login') + async def login(self) -> Response: + dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True)) + result = await self._auth_service.login_async(dto) + return jsonify(result.to_dict()) + + @Route.post(f'{BasePath}/forgot-password/') + async def forgot_password(self, email: str): + await self._auth_service.forgot_password_async(email) + return '', 200 + + @Route.post(f'{BasePath}/confirm-forgot-password/') + async def confirm_forgot_password(self, id: str): + await self._auth_service.confirm_forgot_password_async(id) + return '', 200 + + @Route.post(f'{BasePath}/update-user') + async def update_user(self): + dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True)) + await self._auth_service.update_user_async(dto) + return '', 200 + + @Route.post(f'{BasePath}/update-user-as-admin') + async def update_user_as_admin(self): + dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True)) + await self._auth_service.update_user_async(dto) + return '', 200 + + @Route.post(f'{BasePath}/refresh') + async def refresh(self) -> Response: + dto: TokenDTO = JSONProcessor.process(TokenDTO, request.get_json(force=True, silent=True)) + result = await self._auth_service.refresh_async(dto) + return jsonify(result.to_dict()) + + @Route.post(f'{BasePath}/revoke') + async def revoke(self): + dto: TokenDTO = JSONProcessor.process(TokenDTO, request.get_json(force=True, silent=True)) + await self._auth_service.revoke_async(dto) + return '', 200 + + @Route.post(f'{BasePath}/delete-user') + async def delete_user(self): + dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True)) + await self._auth_service.delete_auth_user_async(dto) + return '', 200 + + @Route.post(f'{BasePath}/delete-user-by-mail/') + async def delete_user_by_mail(self, email: str): + await self._auth_service.delete_auth_user_by_email_async(email) + return '', 200 diff --git a/src/bot_api/json_processor.py b/src/bot_api/json_processor.py new file mode 100644 index 00000000..e629258e --- /dev/null +++ b/src/bot_api/json_processor.py @@ -0,0 +1,32 @@ +from inspect import signature, Parameter + +from cpl_core.utils import String + + +class JSONProcessor: + + @staticmethod + def process(_t: type, values: dict) -> object: + args = [] + + sig = signature(_t.__init__) + for param in sig.parameters.items(): + parameter = param[1] + if parameter.name == 'self' or parameter.annotation == Parameter.empty: + continue + + name = String.convert_to_camel_case(parameter.name) + name_first_lower = String.first_to_lower(name) + if name in values or name_first_lower in values: + if name in values: + args.append(values[name]) + else: + args.append(values[name_first_lower]) + + elif parameter.default != Parameter.empty: + args.append(parameter.default) + + else: + args.append(None) + + return _t(*args) diff --git a/src/bot_api/model/auth_user_dto.py b/src/bot_api/model/auth_user_dto.py index 7ca44a9e..8d7bce2b 100644 --- a/src/bot_api/model/auth_user_dto.py +++ b/src/bot_api/model/auth_user_dto.py @@ -14,7 +14,7 @@ class AuthUserDTO(DtoABC): id: int, first_name: str, last_name: str, - email: str, + e_mail: str, password: str, confirmation_id: Optional[str], auth_role: AuthRoleEnum, @@ -24,20 +24,38 @@ class AuthUserDTO(DtoABC): self._id = id self._first_name = first_name self._last_name = last_name - self._email = email + self._email = e_mail self._password = password self._is_confirmed = confirmation_id is None self._auth_role = auth_role - - """ - long Id { get; set; } - string FirstName { get; set; } - string LastName { get; set; } - string EMail { get; set; } - string Password { get; set; } - bool IsConfirmed { get; set; } - AuthRoles AuthRole { get; set; } - """ + + @property + def id(self) -> int: + return self._id + + @property + def first_name(self) -> str: + return self._first_name + + @property + def last_name(self) -> str: + return self._last_name + + @property + def email(self) -> str: + return self._email + + @property + def password(self) -> str: + return self._password + + @property + def is_confirmed(self) -> bool: + return self._is_confirmed + + @property + def auth_role(self) -> AuthRoleEnum: + return self._auth_role def from_dict(self, values: dict): self._id = values['Id'] diff --git a/src/bot_api/model/auth_user_filtered_result_dto.py b/src/bot_api/model/auth_user_filtered_result_dto.py new file mode 100644 index 00000000..6edbdb44 --- /dev/null +++ b/src/bot_api/model/auth_user_filtered_result_dto.py @@ -0,0 +1,21 @@ +from cpl_query.extension import List + +from bot_api.abc.dto_abc import DtoABC +from bot_data.filtered_result import FilteredResult + + +class AuthUserFilteredResultDTO(DtoABC, FilteredResult): + + def __init__(self, result: List = None, total_count: int = 0): + DtoABC.__init__(self) + FilteredResult.__init__(self, result, total_count) + + def from_dict(self, values: dict): + self._result = values['Users'] + self._total_count = values['TotalCount'] + + def to_dict(self) -> dict: + return { + 'Users': self.result, + 'TotalCount': self.total_count + } diff --git a/src/bot_api/model/email_string_dto.py b/src/bot_api/model/email_string_dto.py new file mode 100644 index 00000000..bc70239d --- /dev/null +++ b/src/bot_api/model/email_string_dto.py @@ -0,0 +1,18 @@ +import traceback + +from cpl_core.console import Console + +from bot_api.abc.dto_abc import DtoABC + + +class EMailStringDTO(DtoABC): + + def __init__(self): + DtoABC.__init__(self) + + def from_dict(self, values: dict): + pass + + def to_dict(self) -> dict: + return { + } diff --git a/src/bot_api/route/route.py b/src/bot_api/route/route.py index 9dbe7fcb..fbf1a9f8 100644 --- a/src/bot_api/route/route.py +++ b/src/bot_api/route/route.py @@ -9,3 +9,23 @@ class Route: return fn return inner + + @classmethod + def get(cls, path=None, **kwargs): + return cls.route(path, methods=['GET'], **kwargs) + + @classmethod + def post(cls, path=None, **kwargs): + return cls.route(path, methods=['POST'], **kwargs) + + @classmethod + def head(cls, path=None, **kwargs): + return cls.route(path, methods=['HEAD'], **kwargs) + + @classmethod + def put(cls, path=None, **kwargs): + return cls.route(path, methods=['PUT'], **kwargs) + + @classmethod + def delete(cls, path=None, **kwargs): + return cls.route(path, methods=['DELETE'], **kwargs) diff --git a/src/bot_api/service/auth_service.py b/src/bot_api/service/auth_service.py index 3455c083..7aa072dc 100644 --- a/src/bot_api/service/auth_service.py +++ b/src/bot_api/service/auth_service.py @@ -2,7 +2,9 @@ from cpl_query.extension import List from bot_api.abc.auth_service_abc import AuthServiceABC from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria +from bot_api.logging.api_logger import ApiLogger from bot_api.model.auth_user_dto import AuthUserDTO +from bot_api.model.auth_user_filtered_result_dto import AuthUserFilteredResultDTO from bot_api.model.reset_password_dto import ResetPasswordDTO from bot_api.model.token_dto import TokenDTO from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO @@ -11,13 +13,18 @@ from bot_data.model.auth_user import AuthUser class AuthService(AuthServiceABC): - def __init__(self): - pass + def __init__( + self, + logger: ApiLogger, + ): + AuthServiceABC.__init__(self) + + self._logger = logger async def get_all_auth_users_async(self) -> List[AuthUser]: pass - async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> List[AuthUser]: + async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO: pass async def get_auth_user_by_email_async(self, email: str) -> AuthUser: @@ -32,7 +39,7 @@ class AuthService(AuthServiceABC): async def confirm_email_async(self, id: str) -> AuthUser: pass - async def login_async(self, user_dto: AuthUserDTO) -> AuthUser: + async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: pass async def forgot_password_async(self, email: str) -> AuthUser: From 1090e502c26b11f67e26c47adaf01488e39f6579 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 14 Oct 2022 23:32:36 +0200 Subject: [PATCH 013/275] [WIP] Added auth service stuff without jwt #70 --- src/bot/translation/de.json | 12 +- src/bot_api/abc/auth_user_transformer_abc.py | 16 ++ src/bot_api/bot-api.json | 1 + src/bot_api/model/email_string_dto.py | 7 +- src/bot_api/model/reset_password_dto.py | 18 +- src/bot_api/model/update_auth_user_dto.py | 31 ++- src/bot_api/service/auth_service.py | 256 ++++++++++++++++-- src/bot_api/transformer/__init__.py | 0 .../transformer/auth_user_transformer.py | 33 +++ src/bot_data/abc/auth_user_repository_abc.py | 10 +- src/bot_data/model/auth_user.py | 60 +++- .../service/auth_user_repository_service.py | 10 +- 12 files changed, 407 insertions(+), 47 deletions(-) create mode 100644 src/bot_api/abc/auth_user_transformer_abc.py create mode 100644 src/bot_api/transformer/__init__.py create mode 100644 src/bot_api/transformer/auth_user_transformer.py diff --git a/src/bot/translation/de.json b/src/bot/translation/de.json index 03b69e95..5ffe9dcf 100644 --- a/src/bot/translation/de.json +++ b/src/bot/translation/de.json @@ -159,6 +159,16 @@ "subject": "Krümmelmonster Web Interface Test-Mail", "message": "Dies ist eine Test-Mail vom Krümmelmonster Web Interface\nGesendet von {}-{}" } - } + }, + "auth": { + "confirmation": { + "subject": "E-Mail für {} {} bestätigen", + "message": "Öffne den Link um die E-Mail zu bestätigen:\n{}auth/forgot-password/{}" + }, + "forgot_password": { + "subject": "Passwort für {} {} zurücksetzen", + "message": "Öffne den Link um das Passwort zu ändern:\n{}auth/forgot-password/{}" + } + } } } \ No newline at end of file diff --git a/src/bot_api/abc/auth_user_transformer_abc.py b/src/bot_api/abc/auth_user_transformer_abc.py new file mode 100644 index 00000000..fd273847 --- /dev/null +++ b/src/bot_api/abc/auth_user_transformer_abc.py @@ -0,0 +1,16 @@ +from abc import abstractmethod + +from cpl_core.database import TableABC + +from bot_api.abc.dto_abc import DtoABC + + +class AuthUserTransformerABC: + + @staticmethod + @abstractmethod + def to_db(dto: DtoABC) -> TableABC: pass + + @staticmethod + @abstractmethod + def to_dto(db: TableABC) -> DtoABC: pass diff --git a/src/bot_api/bot-api.json b/src/bot_api/bot-api.json index 7b66f8ef..a807eeb1 100644 --- a/src/bot_api/bot-api.json +++ b/src/bot_api/bot-api.json @@ -21,6 +21,7 @@ "Flask[async]==2.2.2", "Flask-Classful==0.14.2", "Flask-Cors==3.0.10", + "PyJWT[crypto]==2.5.0", "PyJWT==2.5.0" ], "DevDependencies": [ diff --git a/src/bot_api/model/email_string_dto.py b/src/bot_api/model/email_string_dto.py index bc70239d..1f5ef087 100644 --- a/src/bot_api/model/email_string_dto.py +++ b/src/bot_api/model/email_string_dto.py @@ -7,12 +7,15 @@ from bot_api.abc.dto_abc import DtoABC class EMailStringDTO(DtoABC): - def __init__(self): + def __init__(self, email: str): DtoABC.__init__(self) + self._email = email + def from_dict(self, values: dict): - pass + self._email = values['EMail'] def to_dict(self) -> dict: return { + 'EMail': self._email } diff --git a/src/bot_api/model/reset_password_dto.py b/src/bot_api/model/reset_password_dto.py index 240437cd..0bacce59 100644 --- a/src/bot_api/model/reset_password_dto.py +++ b/src/bot_api/model/reset_password_dto.py @@ -7,12 +7,26 @@ from bot_api.abc.dto_abc import DtoABC class ResetPasswordDTO(DtoABC): - def __init__(self): + def __init__(self, id: str, password: str): DtoABC.__init__(self) + self._id = id + self._password = password + + @property + def id(self) -> str: + return self._id + + @property + def password(self) -> str: + return self._password + def from_dict(self, values: dict): - pass + self._id = values['Id'] + self._password = values['Password'] def to_dict(self) -> dict: return { + 'Id': self._id, + 'Password': self._password } diff --git a/src/bot_api/model/update_auth_user_dto.py b/src/bot_api/model/update_auth_user_dto.py index b7d501f5..7c481330 100644 --- a/src/bot_api/model/update_auth_user_dto.py +++ b/src/bot_api/model/update_auth_user_dto.py @@ -3,16 +3,43 @@ import traceback from cpl_core.console import Console from bot_api.abc.dto_abc import DtoABC +from bot_api.model.auth_user_dto import AuthUserDTO class UpdateAuthUserDTO(DtoABC): - def __init__(self): + def __init__( + self, + auth_user: AuthUserDTO, + new_auth_user: AuthUserDTO, + change_password=False + ): DtoABC.__init__(self) + self._auth_user = auth_user + self._new_auth_user = new_auth_user + self._change_password = change_password + + @property + def auth_user(self) -> AuthUserDTO: + return self._auth_user + + @property + def new_auth_user(self) -> AuthUserDTO: + return self._new_auth_user + + @property + def change_password(self) -> bool: + return self._change_password + def from_dict(self, values: dict): - pass + self._auth_user = values['AuthUser'] + self._new_auth_user = values['NewAuthUser'] + self._change_password = False if 'ChangePassword' not in values else values['ChangePassword'] def to_dict(self) -> dict: return { + 'AuthUser': self._auth_user, + 'NewAuthUser': self._new_auth_user, + 'ChangePassword': self._change_password } diff --git a/src/bot_api/service/auth_service.py b/src/bot_api/service/auth_service.py index 7aa072dc..05283d4e 100644 --- a/src/bot_api/service/auth_service.py +++ b/src/bot_api/service/auth_service.py @@ -1,13 +1,25 @@ +import hashlib +import re +import uuid +from typing import Optional + +from cpl_core.database.context import DatabaseContextABC +from cpl_core.mailing import EMailClientABC, EMail from cpl_query.extension import List +from cpl_translation import TranslatePipe from bot_api.abc.auth_service_abc import AuthServiceABC +from bot_api.configuration.frontend_settings import FrontendSettings from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria from bot_api.logging.api_logger import ApiLogger -from bot_api.model.auth_user_dto import AuthUserDTO +from bot_api.model.auth_user import AuthUserDTO from bot_api.model.auth_user_filtered_result_dto import AuthUserFilteredResultDTO +from bot_api.model.email_string_dto import EMailStringDTO from bot_api.model.reset_password_dto import ResetPasswordDTO from bot_api.model.token_dto import TokenDTO from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO +from bot_api.transformer.auth_user_transformer import AuthUserTransformer as AUT +from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC from bot_data.model.auth_user import AuthUser @@ -16,46 +28,235 @@ class AuthService(AuthServiceABC): def __init__( self, logger: ApiLogger, + auth_users: AuthUserRepositoryABC, + db: DatabaseContextABC, + mailer: EMailClientABC, + t: TranslatePipe, + frontend_settings: FrontendSettings, + ): AuthServiceABC.__init__(self) self._logger = logger + self._auth_users = auth_users + self._db = db + self._mailer = mailer + self._t = t + self._frontend_settings = frontend_settings + + @staticmethod + def _get_mail_to_send() -> EMail: + mail = EMail() + mail.add_header('Mime-Version: 1.0') + mail.add_header('Content-Type: text/plain charset=utf-8') + mail.add_header('Content-Transfer-Encoding: quoted-printable') + return mail + + @staticmethod + def _hash_sha256(password: str) -> str: + return hashlib.sha256(password.encode('utf-8')).hexdigest() + + @staticmethod + def _is_email_valid(email: str) -> bool: + regex = '^[a-z0-9]+[\\._]?[a-z0-9]+[@]\\w+[.]\\w{2,3}$' + return bool(re.search(regex, email)) + + def _send_confirmation_id_to_user(self, user: AuthUser): + url = self._frontend_settings.url + if not url.endswith('/'): + url = f'{url}/' + + mail = self._get_mail_to_send() + mail.add_receiver(user.email) + mail.subject = self._t.transform('api.auth.confirmation.subject').format(user.first_name, user.last_name) + mail.body = self._t.transform('api.auth.confirmation.message').format(url, user.confirmation_id) + self._mailer.send_mail(mail) + + def _send_forgot_password_id_to_user(self, user: AuthUser): + url = self._frontend_settings.url + if not url.endswith('/'): + url = f'{url}/' + + mail = self._get_mail_to_send() + mail.add_receiver(user.email) + mail.subject = self._t.transform('api.auth.forgot_password.subject').format(user.first_name, user.last_name) + mail.body = self._t.transform('api.auth.forgot_password.message').format(url, user.forgot_password_id) + self._mailer.send_mail(mail) async def get_all_auth_users_async(self) -> List[AuthUser]: - pass + result = self._auth_users.get_all_auth_users() \ + .select(lambda x: AUT.to_dto(x)) + return List(AuthUserDTO, result) async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO: - pass + users = self._auth_users.get_filtered_auth_users(criteria) + result = users.result.select(lambda x: AUT.to_dto(x)) + + return AuthUserFilteredResultDTO( + List(AuthUserDTO, result), + users.total_count + ) async def get_auth_user_by_email_async(self, email: str) -> AuthUser: - pass + try: + user = self._auth_users.get_auth_user_by_email(email) + return user + except Exception as e: + self._logger.error(__name__, f'AuthUser not found', e) + raise Exception(f'User not found {email}') - async def find_auth_user_by_email_async(self, email: str) -> AuthUser: - pass + async def find_auth_user_by_email_async(self, email: str) -> Optional[AuthUser]: + user = self._auth_users.find_auth_user_by_email(email) + return AUT.to_dto(user) if user is not None else None async def add_auth_user_async(self, user_dto: AuthUserDTO) -> AuthUser: pass - async def confirm_email_async(self, id: str) -> AuthUser: - pass + async def confirm_email_async(self, id: str) -> bool: + user = self._auth_users.find_auth_user_by_confirmation_id(id) + if user is None: + return False + + user.confirmation_id = None + self._auth_users.update_auth_user(user) + self._db.save_changes() + return True async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: pass - async def forgot_password_async(self, email: str) -> AuthUser: - pass + async def forgot_password_async(self, email: str): + user = self._auth_users.find_auth_user_by_email(email) + if user is None: + return - async def confirm_forgot_password_async(self, id: str) -> AuthUser: - pass + user.forgot_password_id = uuid.uuid4() + self._auth_users.update_auth_user(user) + self._send_forgot_password_id_to_user(user) + self._db.save_changes() - async def reset_password_async(self, rp_dto: ResetPasswordDTO) -> AuthUser: - pass + async def confirm_forgot_password_async(self, id: str) -> EMailStringDTO: + user = self._auth_users.find_auth_user_by_forgot_password_id(id) + return EMailStringDTO(user.email) - async def update_user_async(self, update_user_dto: UpdateAuthUserDTO) -> AuthUser: - pass + async def reset_password_async(self, rp_dto: ResetPasswordDTO): + user = self._auth_users.find_auth_user_by_forgot_password_id(rp_dto.id) + if user is None: + pass - async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO) -> AuthUser: - pass + if user.confirmation_id is not None: + pass + + if user.password is None or rp_dto.password == '': + pass + + user.password = self._hash_sha256(rp_dto.password) + self._db.save_changes() + + async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): + if update_user_dto is None: + raise Exception(f'User is empty') + + if update_user_dto.auth_user is None: + raise Exception(f'Existing user is empty') + + if update_user_dto.new_auth_user is None: + raise Exception(f'New user is empty') + + if not self._is_email_valid(update_user_dto.auth_user.email) or not self._is_email_valid(update_user_dto.new_auth_user.email): + raise Exception(f'Invalid E-Mail') + + user = self._auth_users.find_auth_user_by_email(update_user_dto.auth_user.email) + if user is None: + raise Exception('User not found') + + if user.confirmation_id is not None: + raise Exception('E-Mail not confirmed') + + # update first name + if update_user_dto.new_auth_user.first_name is not None and update_user_dto.auth_user.first_name != update_user_dto.new_auth_user.first_name: + user.FirstName = update_user_dto.new_auth_user.first_name + + # update last name + if update_user_dto.new_auth_user.last_name is not None and update_user_dto.new_auth_user.last_name != '' and \ + update_user_dto.auth_user.last_name != update_user_dto.new_auth_user.last_name: + user.LastName = update_user_dto.new_auth_user.last_name + + # update E-Mail + if update_user_dto.new_auth_user.email is not None and update_user_dto.new_auth_user.email != '' and update_user_dto.auth_user.email != update_user_dto.new_auth_user.email: + user_by_new_e_mail = self._auth_users.find_auth_user_by_email(update_user_dto.new_auth_user.email) + if user_by_new_e_mail is not None: + raise Exception('User already exists') + user.email = update_user_dto.new_auth_user.email + + is_existing_password_set = False + is_new_password_set = False + # hash passwords in DTOs + if update_user_dto.auth_user.Password is not None and update_user_dto.auth_user.Password != '': + is_existing_password_set = True + update_user_dto.auth_user.Password = self._hash_sha256(update_user_dto.auth_user.Password) + + if update_user_dto.auth_user.Password != user.Password: + raise Exception('Wrong password') + + if update_user_dto.new_auth_user.Password is not None and update_user_dto.new_auth_user.Password != '': + is_new_password_set = True + update_user_dto.new_auth_user.Password = self._hash_sha256(update_user_dto.new_auth_user.Password) + + # update password + if is_existing_password_set and is_new_password_set and update_user_dto.auth_user.Password != update_user_dto.new_auth_user.Password: + user.Password = update_user_dto.new_auth_user.Password + + self._db.save_changes() + + async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO): + if update_user_dto is None: + raise Exception(f'User is empty') + + if update_user_dto.auth_user is None: + raise Exception(f'Existing user is empty') + + if update_user_dto.new_auth_user is None: + raise Exception(f'New user is empty') + + if not self._is_email_valid(update_user_dto.auth_user.email) or not self._is_email_valid(update_user_dto.new_auth_user.email): + raise Exception(f'Invalid E-Mail') + + user = self._auth_users.find_auth_user_by_email(update_user_dto.auth_user.email) + if user is None: + raise Exception('User not found') + + if user.ConfirmationId is not None and update_user_dto.new_auth_user.is_confirmed: + user.ConfirmationId = None + elif user.ConfirmationId is None and not update_user_dto.new_auth_user.is_confirmed: + user.confirmation_id = uuid.uuid4() + # else + # raise Exception(ServiceErrorCode.InvalidUser, 'E-Mail not confirmed') + + # update first name + if update_user_dto.new_auth_user.first_name is not None and update_user_dto.auth_user.first_name != update_user_dto.new_auth_user.first_name: + user.FirstName = update_user_dto.new_auth_user.first_name + + # update last name + if update_user_dto.new_auth_user.last_name is not None and update_user_dto.new_auth_user.last_name != '' and update_user_dto.auth_user.last_name != update_user_dto.new_auth_user.last_name: + user.LastName = update_user_dto.new_auth_user.last_name + + # update E-Mail + if update_user_dto.new_auth_user.email is not None and update_user_dto.new_auth_user.email != '' and update_user_dto.auth_user.email != update_user_dto.new_auth_user.email: + user_by_new_e_mail = self._auth_users.find_auth_user_by_email(update_user_dto.new_auth_user.email) + if user_by_new_e_mail is not None: + raise Exception('User already exists') + user.EMail = update_user_dto.new_auth_user.email + + # update password + if update_user_dto.change_password and update_user_dto.auth_user.password != update_user_dto.new_auth_user.password: + user.Password = self._hash_sha256(update_user_dto.new_auth_user.password) + + # update role + if user.auth_role == update_user_dto.auth_user.auth_role and user.auth_role != update_user_dto.new_auth_user.auth_role: + user.auth_role = update_user_dto.new_auth_user.auth_role + + self._db.save_changes() async def refresh_async(self, token_dto: TokenDTO) -> AuthUser: pass @@ -63,8 +264,19 @@ class AuthService(AuthServiceABC): async def revoke_async(self, token_dto: TokenDTO) -> AuthUser: pass - async def delete_auth_user_by_email_async(self, email: str) -> AuthUser: - pass + async def delete_auth_user_by_email_async(self, email: str): + try: + user = self._auth_users.get_auth_user_by_email(email) + self._auth_users.delete_auth_user(user) + self._db.save_changes() + except Exception as e: + self._logger.error(__name__, f'Cannot delete user', e) + raise Exception(f'Cannot delete user by mail {email}') - async def delete_auth_user_async(self, user_dto: AuthUserDTO) -> AuthUser: - pass + async def delete_auth_user_async(self, user_dto: AuthUserDTO): + try: + self._auth_users.delete_auth_user(AUT.to_db(user_dto)) + self._db.save_changes() + except Exception as e: + self._logger.error(__name__, f'Cannot delete user', e) + raise Exception(f'Cannot delete user by mail {user_dto.email}') diff --git a/src/bot_api/transformer/__init__.py b/src/bot_api/transformer/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/bot_api/transformer/auth_user_transformer.py b/src/bot_api/transformer/auth_user_transformer.py new file mode 100644 index 00000000..50258b01 --- /dev/null +++ b/src/bot_api/transformer/auth_user_transformer.py @@ -0,0 +1,33 @@ +from bot_api.abc.auth_user_transformer_abc import AuthUserTransformerABC +from bot_api.model.auth_user_dto import AuthUserDTO +from bot_data.model.auth_user import AuthUser + + +class AuthUserTransformer(AuthUserTransformerABC): + + @staticmethod + def to_db(dto: AuthUserDTO) -> AuthUser: + return AuthUser( + dto.first_name, + dto.last_name, + dto.email, + dto.password, + None, + None, + None, + None, + dto.auth_role, + id=dto.id + ) + + @staticmethod + def to_dto(db: AuthUser) -> AuthUserDTO: + return AuthUserDTO( + db.id, + db.first_name, + db.last_name, + db.email, + db.password, + db.confirmation_id is None, + db.auth_role + ) diff --git a/src/bot_data/abc/auth_user_repository_abc.py b/src/bot_data/abc/auth_user_repository_abc.py index 16118746..a376a273 100644 --- a/src/bot_data/abc/auth_user_repository_abc.py +++ b/src/bot_data/abc/auth_user_repository_abc.py @@ -17,19 +17,19 @@ class AuthUserRepositoryABC(ABC): def get_all_auth_users(self) -> List[AuthUser]: pass @abstractmethod - def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> FilteredResult: pass + def get_filtered_auth_users(self, criteria: AuthUserSelectCriteria) -> FilteredResult: pass @abstractmethod - def get_auth_user_by_email_async(self, email: str) -> AuthUser: pass + def get_auth_user_by_email(self, email: str) -> AuthUser: pass @abstractmethod - def find_auth_user_by_email_async(self, email: str) -> Optional[AuthUser]: pass + def find_auth_user_by_email(self, email: str) -> Optional[AuthUser]: pass @abstractmethod - def find_auth_user_by_confirmation_id_async(self, id: str) -> Optional[AuthUser]: pass + def find_auth_user_by_confirmation_id(self, id: str) -> Optional[AuthUser]: pass @abstractmethod - def find_auth_user_by_forgot_password_id_async(self, id: str) -> Optional[AuthUser]: pass + def find_auth_user_by_forgot_password_id(self, id: str) -> Optional[AuthUser]: pass @abstractmethod def add_auth_user(self, user: AuthUser): pass diff --git a/src/bot_data/model/auth_user.py b/src/bot_data/model/auth_user.py index 6078f209..eeb90d76 100644 --- a/src/bot_data/model/auth_user.py +++ b/src/bot_data/model/auth_user.py @@ -14,10 +14,10 @@ class AuthUser(TableABC): last_name: str, email: str, password: str, - refresh_token: str, - confirmation_id: str, - forgot_password_id: str, - refresh_token_expire_time: datetime, + refresh_token: Optional[str], + confirmation_id: Optional[str], + forgot_password_id: Optional[str], + refresh_token_expire_time: Optional[datetime], auth_role: AuthRoleEnum, created_at: datetime = None, modified_at: datetime = None, @@ -47,30 +47,74 @@ class AuthUser(TableABC): def first_name(self) -> str: return self._first_name + @first_name.setter + def first_name(self, value: str): + self._first_name = value + @property def last_name(self) -> str: return self._last_name + @last_name.setter + def last_name(self, value: str): + self._last_name = value + @property def email(self) -> str: return self._email + @email.setter + def email(self, value: str): + self._email = value + @property def password(self) -> str: return self._password - @property - def refresh_token(self) -> str: - return self._refresh_token + @password.setter + def password(self, value: str): + self._password = value @property - def refresh_token_expire_time(self) -> datetime: + def refresh_token(self) -> Optional[str]: + return self._refresh_token + + @refresh_token.setter + def refresh_token(self, value: Optional[str]): + self._refresh_token = value + + @property + def confirmation_id(self) -> Optional[str]: + return self._confirmation_id + + @confirmation_id.setter + def confirmation_id(self, value: Optional[str]): + self._confirmation_id = value + + @property + def forgot_password_id(self) -> Optional[str]: + return self._forgot_password_id + + @forgot_password_id.setter + def forgot_password_id(self, value: Optional[str]): + self._forgot_password_id = value + + @property + def refresh_token_expire_time(self) -> Optional[datetime]: return self._refresh_token_expire_time + @refresh_token_expire_time.setter + def refresh_token_expire_time(self, value: Optional[datetime]): + self._refresh_token_expire_time = value + @property def auth_role(self) -> AuthRoleEnum: return self._auth_role_id + @auth_role.setter + def auth_role(self, value: AuthRoleEnum): + self._auth_role_id = value + @staticmethod def get_select_all_string() -> str: return str(f""" diff --git a/src/bot_data/service/auth_user_repository_service.py b/src/bot_data/service/auth_user_repository_service.py index aca3e94a..2096113e 100644 --- a/src/bot_data/service/auth_user_repository_service.py +++ b/src/bot_data/service/auth_user_repository_service.py @@ -44,7 +44,7 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): return users - def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> FilteredResult: + def get_filtered_auth_users(self, criteria: AuthUserSelectCriteria) -> FilteredResult: users = self.get_all_auth_users() self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_all_string()}') @@ -77,12 +77,12 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): return result - def get_auth_user_by_email_async(self, email: str) -> AuthUser: + def get_auth_user_by_email(self, email: str) -> AuthUser: self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_by_email_string(email)}') result = self._context.select(AuthUser.get_select_by_email_string(email))[0] return self._user_from_result(result) - def find_auth_user_by_email_async(self, email: str) -> Optional[AuthUser]: + def find_auth_user_by_email(self, email: str) -> Optional[AuthUser]: self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_by_email_string(email)}') result = self._context.select(AuthUser.get_select_by_email_string(email)) if result is None or len(result) == 0: @@ -92,7 +92,7 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): return self._user_from_result(result) - def find_auth_user_by_confirmation_id_async(self, id: str) -> Optional[AuthUser]: + def find_auth_user_by_confirmation_id(self, id: str) -> Optional[AuthUser]: self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_by_email_string(id)}') result = self._context.select(AuthUser.get_select_by_email_string(id)) if result is None or len(result) == 0: @@ -102,7 +102,7 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): return self._user_from_result(result) - def find_auth_user_by_forgot_password_id_async(self, id: str) -> Optional[AuthUser]: + def find_auth_user_by_forgot_password_id(self, id: str) -> Optional[AuthUser]: self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_by_email_string(id)}') result = self._context.select(AuthUser.get_select_by_email_string(id)) if result is None or len(result) == 0: From 6f95d0a055e838a4eb4e98c5d92fd63ba8f6dee2 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 15 Oct 2022 00:42:55 +0200 Subject: [PATCH 014/275] Added auth service stuff with jwt #70 --- src/bot_api/abc/auth_service_abc.py | 38 +++--- src/bot_api/model/auth_user_dto.py | 40 ++++-- src/bot_api/model/token_dto.py | 18 ++- src/bot_api/service/auth_service.py | 185 ++++++++++++++++++++-------- 4 files changed, 204 insertions(+), 77 deletions(-) diff --git a/src/bot_api/abc/auth_service_abc.py b/src/bot_api/abc/auth_service_abc.py index 45788c9a..e4f611c6 100644 --- a/src/bot_api/abc/auth_service_abc.py +++ b/src/bot_api/abc/auth_service_abc.py @@ -33,11 +33,29 @@ class AuthServiceABC(ABC): async def add_auth_user_async(self, user_dto: AuthUserDTO) -> int: pass @abstractmethod - async def confirm_email_async(self, id: str) -> bool: pass + async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): pass + + @abstractmethod + async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO): pass + + @abstractmethod + async def delete_auth_user_by_email_async(self, email: str): pass + + @abstractmethod + async def delete_auth_user_async(self, user_dto: AuthUserDTO): pass @abstractmethod async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: pass + @abstractmethod + async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO: pass + + @abstractmethod + async def revoke_async(self, token_dto: TokenDTO): pass + + @abstractmethod + async def confirm_email_async(self, id: str) -> bool: pass + @abstractmethod async def forgot_password_async(self, email: str): pass @@ -46,21 +64,3 @@ class AuthServiceABC(ABC): @abstractmethod async def reset_password_async(self, rp_dto: ResetPasswordDTO): pass - - @abstractmethod - async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): pass - - @abstractmethod - async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO): pass - - @abstractmethod - async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO: pass - - @abstractmethod - async def revoke_async(self, token_dto: TokenDTO): pass - - @abstractmethod - async def delete_auth_user_by_email_async(self, email: str): pass - - @abstractmethod - async def delete_auth_user_async(self, user_dto: AuthUserDTO): pass diff --git a/src/bot_api/model/auth_user_dto.py b/src/bot_api/model/auth_user_dto.py index 8d7bce2b..304ce047 100644 --- a/src/bot_api/model/auth_user_dto.py +++ b/src/bot_api/model/auth_user_dto.py @@ -32,30 +32,54 @@ class AuthUserDTO(DtoABC): @property def id(self) -> int: return self._id - + @property def first_name(self) -> str: return self._first_name - + + @first_name.setter + def first_name(self, value: str): + self._first_name = value + @property def last_name(self) -> str: return self._last_name - + + @last_name.setter + def last_name(self, value: str): + self._last_name = value + @property def email(self) -> str: return self._email - + + @email.setter + def email(self, value: str): + self._email = value + @property def password(self) -> str: return self._password - + + @password.setter + def password(self, value: str): + self._password = value + @property - def is_confirmed(self) -> bool: + def is_confirmed(self) -> Optional[str]: return self._is_confirmed - + + @is_confirmed.setter + def is_confirmed(self, value: Optional[str]): + self._is_confirmed = value + @property def auth_role(self) -> AuthRoleEnum: - return self._auth_role + return self.auth_role + + @auth_role.setter + def auth_role(self, value: AuthRoleEnum): + self.auth_role = value def from_dict(self, values: dict): self._id = values['Id'] diff --git a/src/bot_api/model/token_dto.py b/src/bot_api/model/token_dto.py index 445943c2..987d9ad2 100644 --- a/src/bot_api/model/token_dto.py +++ b/src/bot_api/model/token_dto.py @@ -7,12 +7,26 @@ from bot_api.abc.dto_abc import DtoABC class TokenDTO(DtoABC): - def __init__(self): + def __init__(self, token: str, refresh_token: str): DtoABC.__init__(self) + self._token = token + self._refresh_token = refresh_token + + @property + def token(self) -> str: + return self._token + + @property + def refresh_token(self) -> str: + return self._refresh_token + def from_dict(self, values: dict): - pass + self._token = values['Token'] + self._refresh_token = values['RefreshToken'] def to_dict(self) -> dict: return { + 'Token': self._token, + 'RefreshToken': self._refresh_token } diff --git a/src/bot_api/service/auth_service.py b/src/bot_api/service/auth_service.py index 05283d4e..47258590 100644 --- a/src/bot_api/service/auth_service.py +++ b/src/bot_api/service/auth_service.py @@ -1,18 +1,21 @@ import hashlib import re import uuid +from datetime import datetime, timedelta, timezone from typing import Optional +import jwt from cpl_core.database.context import DatabaseContextABC from cpl_core.mailing import EMailClientABC, EMail from cpl_query.extension import List from cpl_translation import TranslatePipe from bot_api.abc.auth_service_abc import AuthServiceABC +from bot_api.configuration.authentication_settings import AuthenticationSettings from bot_api.configuration.frontend_settings import FrontendSettings from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria from bot_api.logging.api_logger import ApiLogger -from bot_api.model.auth_user import AuthUserDTO +from bot_api.model.auth_user_dto import AuthUserDTO from bot_api.model.auth_user_filtered_result_dto import AuthUserFilteredResultDTO from bot_api.model.email_string_dto import EMailStringDTO from bot_api.model.reset_password_dto import ResetPasswordDTO @@ -32,6 +35,7 @@ class AuthService(AuthServiceABC): db: DatabaseContextABC, mailer: EMailClientABC, t: TranslatePipe, + auth_settings: AuthenticationSettings, frontend_settings: FrontendSettings, ): @@ -42,6 +46,7 @@ class AuthService(AuthServiceABC): self._db = db self._mailer = mailer self._t = t + self._auth_settings = auth_settings self._frontend_settings = frontend_settings @staticmethod @@ -61,6 +66,29 @@ class AuthService(AuthServiceABC): regex = '^[a-z0-9]+[\\._]?[a-z0-9]+[@]\\w+[.]\\w{2,3}$' return bool(re.search(regex, email)) + def _generate_token(self, user: AuthUser) -> str: + token = jwt.encode( + payload={ + 'user_id': user.id, + 'email': user.email, + 'role': user.auth_role, + 'exp': datetime.now(tz=timezone.utc) + timedelta(days=self._auth_settings.token_expire_time), + 'iss': self._auth_settings.issuer, + 'aud': self._auth_settings.audience + }, + key=self._auth_settings.secret_key, + ) + + return token + + def _create_and_save_refresh_token(self, user: AuthUser) -> str: + token = str(uuid.uuid4()) + user.refresh_token = token + user.refresh_token_expire_time = datetime.now(tz=timezone.utc) + timedelta(days=self._auth_settings.refresh_token_expire_time) + self._auth_users.update_auth_user(user) + self._db.save_changes() + return token + def _send_confirmation_id_to_user(self, user: AuthUser): url = self._frontend_settings.url if not url.endswith('/'): @@ -109,49 +137,24 @@ class AuthService(AuthServiceABC): user = self._auth_users.find_auth_user_by_email(email) return AUT.to_dto(user) if user is not None else None - async def add_auth_user_async(self, user_dto: AuthUserDTO) -> AuthUser: - pass + async def add_auth_user_async(self, user_dto: AuthUserDTO): + db_user = self._auth_users.find_auth_user_by_email(user_dto.email) + if db_user is not None: + raise Exception('User already exists') - async def confirm_email_async(self, id: str) -> bool: - user = self._auth_users.find_auth_user_by_confirmation_id(id) - if user is None: - return False + user_dto.password = self._hash_sha256(user_dto.password) + user = AUT.to_db(user_dto) + if not self._is_email_valid(user.email): + raise Exception('Invalid E-Mail address') - user.confirmation_id = None - self._auth_users.update_auth_user(user) - self._db.save_changes() - return True - - async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: - pass - - async def forgot_password_async(self, email: str): - user = self._auth_users.find_auth_user_by_email(email) - if user is None: - return - - user.forgot_password_id = uuid.uuid4() - self._auth_users.update_auth_user(user) - self._send_forgot_password_id_to_user(user) - self._db.save_changes() - - async def confirm_forgot_password_async(self, id: str) -> EMailStringDTO: - user = self._auth_users.find_auth_user_by_forgot_password_id(id) - return EMailStringDTO(user.email) - - async def reset_password_async(self, rp_dto: ResetPasswordDTO): - user = self._auth_users.find_auth_user_by_forgot_password_id(rp_dto.id) - if user is None: - pass - - if user.confirmation_id is not None: - pass - - if user.password is None or rp_dto.password == '': - pass - - user.password = self._hash_sha256(rp_dto.password) - self._db.save_changes() + try: + user.confirmation_id = uuid.uuid4() + self._auth_users.update_auth_user(user) + self._send_confirmation_id_to_user(user) + self._db.save_changes() + self._logger.info(__name__, f'Added auth user with E-Mail: {user_dto.email}') + except Exception as e: + self._logger.error(__name__, f'Cannot add user with E-Mal {user_dto.email}', e) async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): if update_user_dto is None: @@ -258,12 +261,6 @@ class AuthService(AuthServiceABC): self._db.save_changes() - async def refresh_async(self, token_dto: TokenDTO) -> AuthUser: - pass - - async def revoke_async(self, token_dto: TokenDTO) -> AuthUser: - pass - async def delete_auth_user_by_email_async(self, email: str): try: user = self._auth_users.get_auth_user_by_email(email) @@ -280,3 +277,95 @@ class AuthService(AuthServiceABC): except Exception as e: self._logger.error(__name__, f'Cannot delete user', e) raise Exception(f'Cannot delete user by mail {user_dto.email}') + + async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: + if user_dto is None: + raise Exception('User not set') + + db_user = self._auth_users.find_auth_user_by_email(user_dto.email) + if db_user is None: + raise Exception(f'User with E-Mail {user_dto.email} not found') + + user_dto.password = self._hash_sha256(user_dto.password) + if db_user.password != user_dto.password: + raise Exception('Wrong password') + + token = self._generate_token(user_dto) + refresh_token = self._create_and_save_refresh_token(db_user) + if db_user.forgot_password_id is not None: + db_user.forgot_password_id = None + + self._db.save_changes() + return TokenDTO(token, refresh_token) + + async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO: + if token_dto is None: + raise Exception(f'Token not set') + + token = jwt.decode(token_dto.token, key=self._auth_settings.secret_key) + if token is None or 'email' not in token: + raise Exception('Token invalid') + + try: + user = self._auth_users.get_auth_user_by_email(token) + if user is None or user.refresh_token != token_dto.refresh_token or user.refresh_token_expire_time <= datetime.now(): + raise Exception('Token expired') + + return TokenDTO(self._generate_token(user), self._create_and_save_refresh_token(user)) + except Exception as e: + self._logger.error(__name__, f'Refreshing token failed', e) + return TokenDTO('', '') + + async def revoke_async(self, token_dto: TokenDTO): + if token_dto is None or token_dto.token is None or token_dto.refresh_token is None: + raise Exception('Token not set') + + token = jwt.decode(token_dto.token, key=self._auth_settings.secret_key) + try: + user = self._auth_users.get_auth_user_by_email(token) + if user is None or user.refresh_token != token_dto.refresh_token or user.refresh_token_expire_time <= datetime.now(): + raise Exception('Token expired') + + user.refresh_token = None + self._auth_users.update_auth_user(user) + self._db.save_changes() + except Exception as e: + self._logger.error(__name__, f'Refreshing token failed', e) + + async def confirm_email_async(self, id: str) -> bool: + user = self._auth_users.find_auth_user_by_confirmation_id(id) + if user is None: + return False + + user.confirmation_id = None + self._auth_users.update_auth_user(user) + self._db.save_changes() + return True + + async def forgot_password_async(self, email: str): + user = self._auth_users.find_auth_user_by_email(email) + if user is None: + return + + user.forgot_password_id = uuid.uuid4() + self._auth_users.update_auth_user(user) + self._send_forgot_password_id_to_user(user) + self._db.save_changes() + + async def confirm_forgot_password_async(self, id: str) -> EMailStringDTO: + user = self._auth_users.find_auth_user_by_forgot_password_id(id) + return EMailStringDTO(user.email) + + async def reset_password_async(self, rp_dto: ResetPasswordDTO): + user = self._auth_users.find_auth_user_by_forgot_password_id(rp_dto.id) + if user is None: + raise Exception(f'User by forgot password id {rp_dto.id} not found') + + if user.confirmation_id is not None: + raise Exception(f'E-Mail not confirmed') + + if user.password is None or rp_dto.password == '': + raise Exception(f'Password not set') + + user.password = self._hash_sha256(rp_dto.password) + self._db.save_changes() From e0844a7f9267f484bbe617b89482b69d7cdbc30a Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 15 Oct 2022 03:04:17 +0200 Subject: [PATCH 015/275] [WIP] Improved basics of api #70 --- src/bot/startup_migration_extension.py | 2 + src/bot_api/api.py | 26 +++++-- src/bot_api/exception/__init__.py | 0 .../exception/service_error_code_enum.py | 19 ++++++ src/bot_api/exception/service_exception.py | 13 ++++ src/bot_api/model/error_dto.py | 34 ++++++++++ src/bot_api/route/route.py | 4 ++ src/bot_api/service/auth_service.py | 67 ++++++++++--------- src/bot_data/migration/api_migration.py | 4 +- src/bot_data/model/auth_user.py | 28 ++++---- 10 files changed, 145 insertions(+), 52 deletions(-) create mode 100644 src/bot_api/exception/__init__.py create mode 100644 src/bot_api/exception/service_error_code_enum.py create mode 100644 src/bot_api/exception/service_exception.py create mode 100644 src/bot_api/model/error_dto.py diff --git a/src/bot/startup_migration_extension.py b/src/bot/startup_migration_extension.py index b8f7ff0e..62d4d39d 100644 --- a/src/bot/startup_migration_extension.py +++ b/src/bot/startup_migration_extension.py @@ -4,6 +4,7 @@ from cpl_core.dependency_injection import ServiceCollectionABC from cpl_core.environment import ApplicationEnvironmentABC from bot_data.abc.migration_abc import MigrationABC +from bot_data.migration.api_migration import ApiMigration from bot_data.migration.auto_role_migration import AutoRoleMigration from bot_data.migration.initial_migration import InitialMigration from bot_data.service.migration_service import MigrationService @@ -21,3 +22,4 @@ class StartupMigrationExtension(StartupExtensionABC): services.add_transient(MigrationService) services.add_transient(MigrationABC, InitialMigration) services.add_transient(MigrationABC, AutoRoleMigration) # 03.10.2022 #54 - 0.2.2 + services.add_transient(MigrationABC, ApiMigration) # 15.10.2022 #70 - 0.3.0 diff --git a/src/bot_api/api.py b/src/bot_api/api.py index 8760377b..6a08c9a6 100644 --- a/src/bot_api/api.py +++ b/src/bot_api/api.py @@ -1,13 +1,16 @@ +import json import sys +import uuid from functools import partial -from blinker import Namespace from cpl_core.dependency_injection import ServiceProviderABC -from flask import Flask, request +from flask import Flask, request, jsonify, Response, make_response from flask_cors import CORS from bot_api.configuration.api_settings import ApiSettings +from bot_api.exception.service_exception import ServiceException from bot_api.logging.api_logger import ApiLogger +from bot_api.model.error_dto import ErrorDTO from bot_api.route.route import Route @@ -33,8 +36,8 @@ class Api(Flask): # register before request self.before_request_funcs.setdefault(None, []).append(self.before_request) - my_signals = Namespace() - notify = my_signals.signal('notify') + exc_class, code = self._get_exc_class_and_code(Exception) + self.error_handler_spec[None][code][exc_class] = self.handle_exception def _register_routes(self): for path, f in Route.registered_routes.items(): @@ -50,6 +53,21 @@ class Api(Flask): partial_f.__name__ = route.__name__ self.route(path, **kwargs)(partial_f) + def handle_exception(self, e: Exception): + self._logger.error(__name__, f'Caught error', e) + + if isinstance(e, ServiceException): + ex: ServiceException = e + self._logger.error(__name__, ex.get_detailed_message()) + error = ErrorDTO(ex.error_code, ex.message) + return jsonify(error.to_dict()), 500 + else: + tracking_id = uuid.uuid4() + user_message = f'Tracking Id: {tracking_id}' + self._logger.error(__name__, user_message, e) + error = ErrorDTO(None, user_message) + return jsonify(error.to_dict()), 400 + def before_request(self, *args, **kwargs): self._logger.debug(__name__, f'Received GET @{request.url}') headers = str(request.headers).replace("\n", "\n\t") diff --git a/src/bot_api/exception/__init__.py b/src/bot_api/exception/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/bot_api/exception/service_error_code_enum.py b/src/bot_api/exception/service_error_code_enum.py new file mode 100644 index 00000000..24da48ed --- /dev/null +++ b/src/bot_api/exception/service_error_code_enum.py @@ -0,0 +1,19 @@ +from enum import Enum + + +class ServiceErrorCode(Enum): + + Unknown = 0 + + InvalidDependencies = 1 + InvalidData = 2 + NotFound = 3 + DataAlreadyExists = 4 + UnableToAdd = 5 + UnableToDelete = 6 + + InvalidUser = 7 + + ConnectionFailed = 8 + Timeout = 9 + MailError = 10 diff --git a/src/bot_api/exception/service_exception.py b/src/bot_api/exception/service_exception.py new file mode 100644 index 00000000..6451ae50 --- /dev/null +++ b/src/bot_api/exception/service_exception.py @@ -0,0 +1,13 @@ +from bot_api.exception.service_error_code_enum import ServiceErrorCode + + +class ServiceException(Exception): + + def __init__(self, error_code: ServiceErrorCode, message: str, *args): + Exception.__init__(self, *args) + + self.error_code = error_code + self.message = message + + def get_detailed_message(self) -> str: + return f'ServiceException - ErrorCode: {self.error_code} - ErrorMessage: {self.message}' diff --git a/src/bot_api/model/error_dto.py b/src/bot_api/model/error_dto.py new file mode 100644 index 00000000..b55cc1c5 --- /dev/null +++ b/src/bot_api/model/error_dto.py @@ -0,0 +1,34 @@ +import traceback +from typing import Optional + +from cpl_core.console import Console + +from bot_api.abc.dto_abc import DtoABC +from bot_api.exception.service_error_code_enum import ServiceErrorCode + + +class ErrorDTO(DtoABC): + + def __init__(self, error_code: Optional[ServiceErrorCode], message: str): + DtoABC.__init__(self) + + self._error_code = ServiceErrorCode.Unknown if error_code is None else error_code + self._message = message + + @property + def error_code(self) -> ServiceErrorCode: + return self._error_code + + @property + def message(self) -> str: + return self._message + + def from_dict(self, values: dict): + self._error_code = values['ErrorCode'] + self._message = values['Message'] + + def to_dict(self) -> dict: + return { + 'ErrorCode': int(self._error_code.value), + 'Message': self._message + } diff --git a/src/bot_api/route/route.py b/src/bot_api/route/route.py index fbf1a9f8..74fbbc67 100644 --- a/src/bot_api/route/route.py +++ b/src/bot_api/route/route.py @@ -1,3 +1,6 @@ +from flask_cors import cross_origin + + class Route: registered_routes = {} @@ -5,6 +8,7 @@ class Route: def route(cls, path=None, **kwargs): # simple decorator for class based views def inner(fn): + cross_origin(fn) cls.registered_routes[path] = (fn, kwargs) return fn diff --git a/src/bot_api/service/auth_service.py b/src/bot_api/service/auth_service.py index 47258590..61b5cca6 100644 --- a/src/bot_api/service/auth_service.py +++ b/src/bot_api/service/auth_service.py @@ -13,6 +13,8 @@ from cpl_translation import TranslatePipe from bot_api.abc.auth_service_abc import AuthServiceABC from bot_api.configuration.authentication_settings import AuthenticationSettings from bot_api.configuration.frontend_settings import FrontendSettings +from bot_api.exception.service_error_code_enum import ServiceErrorCode +from bot_api.exception.service_exception import ServiceException from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria from bot_api.logging.api_logger import ApiLogger from bot_api.model.auth_user_dto import AuthUserDTO @@ -131,7 +133,7 @@ class AuthService(AuthServiceABC): return user except Exception as e: self._logger.error(__name__, f'AuthUser not found', e) - raise Exception(f'User not found {email}') + raise ServiceException(ServiceErrorCode.InvalidData, f'User not found {email}') async def find_auth_user_by_email_async(self, email: str) -> Optional[AuthUser]: user = self._auth_users.find_auth_user_by_email(email) @@ -140,12 +142,12 @@ class AuthService(AuthServiceABC): async def add_auth_user_async(self, user_dto: AuthUserDTO): db_user = self._auth_users.find_auth_user_by_email(user_dto.email) if db_user is not None: - raise Exception('User already exists') + raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') user_dto.password = self._hash_sha256(user_dto.password) user = AUT.to_db(user_dto) if not self._is_email_valid(user.email): - raise Exception('Invalid E-Mail address') + raise ServiceException(ServiceErrorCode.InvalidData, 'Invalid E-Mail address') try: user.confirmation_id = uuid.uuid4() @@ -155,26 +157,27 @@ class AuthService(AuthServiceABC): self._logger.info(__name__, f'Added auth user with E-Mail: {user_dto.email}') except Exception as e: self._logger.error(__name__, f'Cannot add user with E-Mal {user_dto.email}', e) + raise ServiceException(ServiceErrorCode.UnableToAdd, "Invalid E-Mail") async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): if update_user_dto is None: - raise Exception(f'User is empty') + raise ServiceException(ServiceErrorCode.InvalidData, f'User is empty') if update_user_dto.auth_user is None: - raise Exception(f'Existing user is empty') + raise ServiceException(ServiceErrorCode.InvalidData, f'Existing user is empty') if update_user_dto.new_auth_user is None: - raise Exception(f'New user is empty') + raise ServiceException(ServiceErrorCode.InvalidData, f'New user is empty') if not self._is_email_valid(update_user_dto.auth_user.email) or not self._is_email_valid(update_user_dto.new_auth_user.email): - raise Exception(f'Invalid E-Mail') + raise ServiceException(ServiceErrorCode.InvalidData, f'Invalid E-Mail') user = self._auth_users.find_auth_user_by_email(update_user_dto.auth_user.email) if user is None: - raise Exception('User not found') + raise ServiceException(ServiceErrorCode.InvalidUser, 'User not found') if user.confirmation_id is not None: - raise Exception('E-Mail not confirmed') + raise ServiceException(ServiceErrorCode.InvalidUser, 'E-Mail not confirmed') # update first name if update_user_dto.new_auth_user.first_name is not None and update_user_dto.auth_user.first_name != update_user_dto.new_auth_user.first_name: @@ -189,7 +192,7 @@ class AuthService(AuthServiceABC): if update_user_dto.new_auth_user.email is not None and update_user_dto.new_auth_user.email != '' and update_user_dto.auth_user.email != update_user_dto.new_auth_user.email: user_by_new_e_mail = self._auth_users.find_auth_user_by_email(update_user_dto.new_auth_user.email) if user_by_new_e_mail is not None: - raise Exception('User already exists') + raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') user.email = update_user_dto.new_auth_user.email is_existing_password_set = False @@ -200,7 +203,7 @@ class AuthService(AuthServiceABC): update_user_dto.auth_user.Password = self._hash_sha256(update_user_dto.auth_user.Password) if update_user_dto.auth_user.Password != user.Password: - raise Exception('Wrong password') + raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password') if update_user_dto.new_auth_user.Password is not None and update_user_dto.new_auth_user.Password != '': is_new_password_set = True @@ -214,27 +217,27 @@ class AuthService(AuthServiceABC): async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO): if update_user_dto is None: - raise Exception(f'User is empty') + raise ServiceException(ServiceErrorCode.InvalidData, f'User is empty') if update_user_dto.auth_user is None: - raise Exception(f'Existing user is empty') + raise ServiceException(ServiceErrorCode.InvalidData, f'Existing user is empty') if update_user_dto.new_auth_user is None: - raise Exception(f'New user is empty') + raise ServiceException(ServiceErrorCode.InvalidData, f'New user is empty') if not self._is_email_valid(update_user_dto.auth_user.email) or not self._is_email_valid(update_user_dto.new_auth_user.email): - raise Exception(f'Invalid E-Mail') + raise ServiceException(ServiceErrorCode.InvalidData, f'Invalid E-Mail') user = self._auth_users.find_auth_user_by_email(update_user_dto.auth_user.email) if user is None: - raise Exception('User not found') + raise ServiceException(ServiceErrorCode.InvalidUser, 'User not found') if user.ConfirmationId is not None and update_user_dto.new_auth_user.is_confirmed: user.ConfirmationId = None elif user.ConfirmationId is None and not update_user_dto.new_auth_user.is_confirmed: user.confirmation_id = uuid.uuid4() # else - # raise Exception(ServiceErrorCode.InvalidUser, 'E-Mail not confirmed') + # raise ServiceException(ServiceErrorCode.InvalidUser, 'E-Mail not confirmed') # update first name if update_user_dto.new_auth_user.first_name is not None and update_user_dto.auth_user.first_name != update_user_dto.new_auth_user.first_name: @@ -248,7 +251,7 @@ class AuthService(AuthServiceABC): if update_user_dto.new_auth_user.email is not None and update_user_dto.new_auth_user.email != '' and update_user_dto.auth_user.email != update_user_dto.new_auth_user.email: user_by_new_e_mail = self._auth_users.find_auth_user_by_email(update_user_dto.new_auth_user.email) if user_by_new_e_mail is not None: - raise Exception('User already exists') + raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') user.EMail = update_user_dto.new_auth_user.email # update password @@ -268,7 +271,7 @@ class AuthService(AuthServiceABC): self._db.save_changes() except Exception as e: self._logger.error(__name__, f'Cannot delete user', e) - raise Exception(f'Cannot delete user by mail {email}') + raise ServiceException(ServiceErrorCode.UnableToDelete, f'Cannot delete user by mail {email}') async def delete_auth_user_async(self, user_dto: AuthUserDTO): try: @@ -276,21 +279,21 @@ class AuthService(AuthServiceABC): self._db.save_changes() except Exception as e: self._logger.error(__name__, f'Cannot delete user', e) - raise Exception(f'Cannot delete user by mail {user_dto.email}') + raise ServiceException(ServiceErrorCode.UnableToDelete, f'Cannot delete user by mail {user_dto.email}') async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: if user_dto is None: - raise Exception('User not set') + raise ServiceException(ServiceErrorCode.InvalidData, 'User not set') db_user = self._auth_users.find_auth_user_by_email(user_dto.email) if db_user is None: - raise Exception(f'User with E-Mail {user_dto.email} not found') + raise ServiceException(ServiceErrorCode.InvalidUser, f'User not found') user_dto.password = self._hash_sha256(user_dto.password) if db_user.password != user_dto.password: - raise Exception('Wrong password') + raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password') - token = self._generate_token(user_dto) + token = self._generate_token(db_user) refresh_token = self._create_and_save_refresh_token(db_user) if db_user.forgot_password_id is not None: db_user.forgot_password_id = None @@ -300,16 +303,16 @@ class AuthService(AuthServiceABC): async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO: if token_dto is None: - raise Exception(f'Token not set') + raise ServiceException(ServiceErrorCode.InvalidData, f'Token not set') token = jwt.decode(token_dto.token, key=self._auth_settings.secret_key) if token is None or 'email' not in token: - raise Exception('Token invalid') + raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') try: user = self._auth_users.get_auth_user_by_email(token) if user is None or user.refresh_token != token_dto.refresh_token or user.refresh_token_expire_time <= datetime.now(): - raise Exception('Token expired') + raise ServiceException(ServiceErrorCode.InvalidData, 'Token expired') return TokenDTO(self._generate_token(user), self._create_and_save_refresh_token(user)) except Exception as e: @@ -318,13 +321,13 @@ class AuthService(AuthServiceABC): async def revoke_async(self, token_dto: TokenDTO): if token_dto is None or token_dto.token is None or token_dto.refresh_token is None: - raise Exception('Token not set') + raise ServiceException(ServiceErrorCode.InvalidData, 'Token not set') token = jwt.decode(token_dto.token, key=self._auth_settings.secret_key) try: user = self._auth_users.get_auth_user_by_email(token) if user is None or user.refresh_token != token_dto.refresh_token or user.refresh_token_expire_time <= datetime.now(): - raise Exception('Token expired') + raise ServiceException(ServiceErrorCode.InvalidData, 'Token expired') user.refresh_token = None self._auth_users.update_auth_user(user) @@ -359,13 +362,13 @@ class AuthService(AuthServiceABC): async def reset_password_async(self, rp_dto: ResetPasswordDTO): user = self._auth_users.find_auth_user_by_forgot_password_id(rp_dto.id) if user is None: - raise Exception(f'User by forgot password id {rp_dto.id} not found') + raise ServiceException(ServiceErrorCode.InvalidUser, f'User by forgot password id {rp_dto.id} not found') if user.confirmation_id is not None: - raise Exception(f'E-Mail not confirmed') + raise ServiceException(ServiceErrorCode.InvalidUser, f'E-Mail not confirmed') if user.password is None or rp_dto.password == '': - raise Exception(f'Password not set') + raise ServiceException(ServiceErrorCode.InvalidData, f'Password not set') user.password = self._hash_sha256(rp_dto.password) self._db.save_changes() diff --git a/src/bot_data/migration/api_migration.py b/src/bot_data/migration/api_migration.py index 187dc5fc..61f9d156 100644 --- a/src/bot_data/migration/api_migration.py +++ b/src/bot_data/migration/api_migration.py @@ -27,9 +27,9 @@ class ApiMigration(MigrationABC): `ConfirmationId` varchar(255) DEFAULT NULL, `ForgotPasswordId` varchar(255) DEFAULT NULL, `RefreshTokenExpiryTime` datetime(6) NOT NULL, - `AuthRole` int NOT NULL DEFAULT '0' + `AuthRole` int NOT NULL DEFAULT '0', `CreatedOn` datetime(6) NOT NULL, - `LastModifiedOn` datetime(6) NOT NULL, + `LastModifiedOn` datetime(6) NOT NULL ) """) ) diff --git a/src/bot_data/model/auth_user.py b/src/bot_data/model/auth_user.py index eeb90d76..57cdde0a 100644 --- a/src/bot_data/model/auth_user.py +++ b/src/bot_data/model/auth_user.py @@ -132,21 +132,21 @@ class AuthUser(TableABC): def get_select_by_email_string(email: str) -> str: return str(f""" SELECT * FROM `AuthUsers` - WHERE `EMail` = {email}; + WHERE `EMail` = '{email}'; """) @staticmethod def get_select_by_confirmation_id_string(id: str) -> str: return str(f""" SELECT * FROM `AuthUsers` - WHERE `ConfirmationId` = {id}; + WHERE `ConfirmationId` = '{id}'; """) @staticmethod def get_select_by_forgot_password_i_string(id: str) -> str: return str(f""" SELECT * FROM `AuthUsers` - WHERE `ForgotPasswordId` = {id}; + WHERE `ForgotPasswordId` = '{id}'; """) @property @@ -167,17 +167,17 @@ class AuthUser(TableABC): `LastModifiedOn` ) VALUES ( {self._auth_user_id}, - {self._first_name}, - {self._last_name}, - {self._email}, - {self._password}, - {self._refresh_token}, - {self._confirmation_id}, - {self._forgot_password_id}, - {self._refresh_token_expire_time}, - {self._auth_role_id.value} - {self._created_at}, - {self._modified_at} + '{self._first_name}', + '{self._last_name}', + '{self._email}', + '{self._password}', + '{self._refresh_token}', + '{self._confirmation_id}', + '{self._forgot_password_id}', + '{self._refresh_token_expire_time}', + {self._auth_role_id.value}, + '{self._created_at}', + '{self._modified_at}' ) """) From 5cd91d8341660045bb8b707a95a24a7f70eae09c Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 15 Oct 2022 09:32:41 +0200 Subject: [PATCH 016/275] Fixed error and dto handling #70 --- src/bot_api/model/auth_user_dto.py | 28 +++++------ .../model/auth_user_filtered_result_dto.py | 8 ++-- src/bot_api/model/email_string_dto.py | 4 +- src/bot_api/model/error_dto.py | 4 +- src/bot_api/model/reset_password_dto.py | 8 ++-- src/bot_api/model/settings_dto.py | 48 +++++++++---------- src/bot_api/model/token_dto.py | 8 ++-- src/bot_api/model/update_auth_user_dto.py | 12 ++--- src/bot_api/model/version_dto.py | 12 ++--- 9 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/bot_api/model/auth_user_dto.py b/src/bot_api/model/auth_user_dto.py index 304ce047..19923890 100644 --- a/src/bot_api/model/auth_user_dto.py +++ b/src/bot_api/model/auth_user_dto.py @@ -82,21 +82,21 @@ class AuthUserDTO(DtoABC): self.auth_role = value def from_dict(self, values: dict): - self._id = values['Id'] - self._first_name = values['FirstName'] - self._last_name = values['LastName'] - self._email = values['EMail'] - self._password = values['Password'] - self._is_confirmed = values['IsConfirmed'] - self._auth_role = values['AuthRole'] + self._id = values['id'] + self._first_name = values['firstName'] + self._last_name = values['lastName'] + self._email = values['email'] + self._password = values['password'] + self._is_confirmed = values['isConfirmed'] + self._auth_role = values['authRole'] def to_dict(self) -> dict: return { - "Id": self._id, - "FirstName": self._first_name, - "LastName": self._last_name, - "EMail": self._email, - "Password": self._password, - "IsConfirmed": self._is_confirmed, - "AuthRole": self._auth_role, + 'id': self._id, + 'firstName': self._first_name, + 'lastName': self._last_name, + 'email': self._email, + 'password': self._password, + 'isConfirmed': self._is_confirmed, + 'authRole': self._auth_role, } diff --git a/src/bot_api/model/auth_user_filtered_result_dto.py b/src/bot_api/model/auth_user_filtered_result_dto.py index 6edbdb44..a3125d25 100644 --- a/src/bot_api/model/auth_user_filtered_result_dto.py +++ b/src/bot_api/model/auth_user_filtered_result_dto.py @@ -11,11 +11,11 @@ class AuthUserFilteredResultDTO(DtoABC, FilteredResult): FilteredResult.__init__(self, result, total_count) def from_dict(self, values: dict): - self._result = values['Users'] - self._total_count = values['TotalCount'] + self._result = values['users'] + self._total_count = values['totalCount'] def to_dict(self) -> dict: return { - 'Users': self.result, - 'TotalCount': self.total_count + 'users': self.result, + 'totalCount': self.total_count } diff --git a/src/bot_api/model/email_string_dto.py b/src/bot_api/model/email_string_dto.py index 1f5ef087..5ad6eecb 100644 --- a/src/bot_api/model/email_string_dto.py +++ b/src/bot_api/model/email_string_dto.py @@ -13,9 +13,9 @@ class EMailStringDTO(DtoABC): self._email = email def from_dict(self, values: dict): - self._email = values['EMail'] + self._email = values['email'] def to_dict(self) -> dict: return { - 'EMail': self._email + 'email': self._email } diff --git a/src/bot_api/model/error_dto.py b/src/bot_api/model/error_dto.py index b55cc1c5..39c7d6a1 100644 --- a/src/bot_api/model/error_dto.py +++ b/src/bot_api/model/error_dto.py @@ -29,6 +29,6 @@ class ErrorDTO(DtoABC): def to_dict(self) -> dict: return { - 'ErrorCode': int(self._error_code.value), - 'Message': self._message + 'errorCode': int(self._error_code.value), + 'message': self._message } diff --git a/src/bot_api/model/reset_password_dto.py b/src/bot_api/model/reset_password_dto.py index 0bacce59..64de816d 100644 --- a/src/bot_api/model/reset_password_dto.py +++ b/src/bot_api/model/reset_password_dto.py @@ -22,11 +22,11 @@ class ResetPasswordDTO(DtoABC): return self._password def from_dict(self, values: dict): - self._id = values['Id'] - self._password = values['Password'] + self._id = values['id'] + self._password = values['password'] def to_dict(self) -> dict: return { - 'Id': self._id, - 'Password': self._password + 'id': self._id, + 'password': self._password } diff --git a/src/bot_api/model/settings_dto.py b/src/bot_api/model/settings_dto.py index ce3ec436..567f3931 100644 --- a/src/bot_api/model/settings_dto.py +++ b/src/bot_api/model/settings_dto.py @@ -37,31 +37,31 @@ class SettingsDTO(DtoABC): self._mail_transceiver_address = mail_transceiver_address def from_dict(self, values: dict): - self._web_version = values['WebVersion'] - self._api_version.from_dict(values['ApiVersion']) - self._config_path = values['ConfigPath'] - self._web_base_url = values['WebBaseURL'] - self._api_base_url = values['ApiBaseURL'] - self._token_expire_time = values['TokenExpireTime'] - self._refresh_token_expire_time = values['RefreshTokenExpireTime'] - self._mail_user = values['MailUser'] - self._mail_port = values['MailPort'] - self._mail_host = values['MailHost'] - self._mail_transceiver = values['MailTransceiver'] - self._mail_transceiver_address = values['MailTransceiverAddress'] + self._web_version = values['webVersion'] + self._api_version.from_dict(values['apiVersion']) + self._config_path = values['configPath'] + self._web_base_url = values['webBaseURL'] + self._api_base_url = values['apiBaseURL'] + self._token_expire_time = values['tokenExpireTime'] + self._refresh_token_expire_time = values['refreshTokenExpireTime'] + self._mail_user = values['mailUser'] + self._mail_port = values['mailPort'] + self._mail_host = values['mailHost'] + self._mail_transceiver = values['mailTransceiver'] + self._mail_transceiver_address = values['mailTransceiverAddress'] def to_dict(self) -> dict: return { - 'WebVersion': self._web_version, - 'ApiVersion': self._api_version.to_dict(), - 'ConfigPath': self._config_path, - 'WebBaseURL': self._web_base_url, - 'ApiBaseURL': self._api_base_url, - 'TokenExpireTime': self._token_expire_time, - 'RefreshTokenExpireTime': self._refresh_token_expire_time, - 'MailUser': self._mail_user, - 'MailPort': self._mail_port, - 'MailHost': self._mail_host, - 'MailTransceiver': self._mail_transceiver, - 'MailTransceiverAddress': self._mail_transceiver_address, + 'webVersion': self._web_version, + 'apiVersion': self._api_version.to_dict(), + 'configPath': self._config_path, + 'webBaseURL': self._web_base_url, + 'apiBaseURL': self._api_base_url, + 'tokenExpireTime': self._token_expire_time, + 'refreshTokenExpireTime': self._refresh_token_expire_time, + 'mailUser': self._mail_user, + 'mailPort': self._mail_port, + 'mailHost': self._mail_host, + 'mailTransceiver': self._mail_transceiver, + 'mailTransceiverAddress': self._mail_transceiver_address, } diff --git a/src/bot_api/model/token_dto.py b/src/bot_api/model/token_dto.py index 987d9ad2..d5fc3087 100644 --- a/src/bot_api/model/token_dto.py +++ b/src/bot_api/model/token_dto.py @@ -22,11 +22,11 @@ class TokenDTO(DtoABC): return self._refresh_token def from_dict(self, values: dict): - self._token = values['Token'] - self._refresh_token = values['RefreshToken'] + self._token = values['token'] + self._refresh_token = values['refreshToken'] def to_dict(self) -> dict: return { - 'Token': self._token, - 'RefreshToken': self._refresh_token + 'token': self._token, + 'refreshToken': self._refresh_token } diff --git a/src/bot_api/model/update_auth_user_dto.py b/src/bot_api/model/update_auth_user_dto.py index 7c481330..682f0122 100644 --- a/src/bot_api/model/update_auth_user_dto.py +++ b/src/bot_api/model/update_auth_user_dto.py @@ -33,13 +33,13 @@ class UpdateAuthUserDTO(DtoABC): return self._change_password def from_dict(self, values: dict): - self._auth_user = values['AuthUser'] - self._new_auth_user = values['NewAuthUser'] - self._change_password = False if 'ChangePassword' not in values else values['ChangePassword'] + self._auth_user = values['authUser'] + self._new_auth_user = values['newAuthUser'] + self._change_password = False if 'changePassword' not in values else values['changePassword'] def to_dict(self) -> dict: return { - 'AuthUser': self._auth_user, - 'NewAuthUser': self._new_auth_user, - 'ChangePassword': self._change_password + 'authUser': self._auth_user, + 'newAuthUser': self._new_auth_user, + 'changePassword': self._change_password } diff --git a/src/bot_api/model/version_dto.py b/src/bot_api/model/version_dto.py index 8ef98dc4..c1504478 100644 --- a/src/bot_api/model/version_dto.py +++ b/src/bot_api/model/version_dto.py @@ -15,13 +15,13 @@ class VersionDTO(DtoABC): self._micro = micro def from_dict(self, values: dict): - self._major = values['Major'] - self._minor = values['Minor'] - self._micro = values['Micro'] + self._major = values['major'] + self._minor = values['minor'] + self._micro = values['micro'] def to_dict(self) -> dict: return { - 'Major': self._major, - 'Minor': self._minor, - 'Micro': self._micro, + 'major': self._major, + 'minor': self._minor, + 'micro': self._micro, } From 119f4e9d04f5a4d8efcad96879a94d09f956210c Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 15 Oct 2022 12:54:10 +0200 Subject: [PATCH 017/275] Improved api #70 --- src/bot/application.py | 2 +- src/bot/bot.json | 2 +- src/bot/translation/de.json | 2 +- src/bot_api/abc/auth_service_abc.py | 7 ++-- src/bot_api/api_module.py | 4 +- .../config/apisettings.edrafts-lapi.json | 2 +- src/bot_api/controller/auth_controller.py | 10 +++-- .../{api_controller.py => gui_controller.py} | 9 ++-- .../filter/auth_user_select_criteria.py | 2 +- src/bot_api/model/auth_user_dto.py | 13 +++--- src/bot_api/service/auth_service.py | 42 ++++++++++++------- .../transformer/auth_user_transformer.py | 13 +++--- src/bot_data/migration/api_migration.py | 25 +++++------ src/bot_data/model/auth_role_enum.py | 4 +- src/bot_data/model/auth_user.py | 20 ++++----- .../service/auth_user_repository_service.py | 39 +++++++++-------- 16 files changed, 108 insertions(+), 88 deletions(-) rename src/bot_api/controller/{api_controller.py => gui_controller.py} (92%) diff --git a/src/bot/application.py b/src/bot/application.py index 726cdbdb..6c65a6b4 100644 --- a/src/bot/application.py +++ b/src/bot/application.py @@ -46,7 +46,7 @@ class Application(DiscordBotApplicationABC): if self._feature_flags.get_flag(FeatureFlagsEnum.api_module): self._api.start() - if self._feature_flags.get_flag(FeatureFlagsEnum.api_only): + if self._feature_flags.get_flag(FeatureFlagsEnum.api_only) and self._environment.environment_name == 'development': self._api.join() return diff --git a/src/bot/bot.json b/src/bot/bot.json index 64495674..df826bb7 100644 --- a/src/bot/bot.json +++ b/src/bot/bot.json @@ -18,7 +18,7 @@ "Dependencies": [ "cpl-core==2022.10.0.post6", "cpl-translation==2022.10.0.post1", - "cpl-query==2022.10.0", + "cpl-query==2022.10.0.post1", "cpl-discord==2022.10.0.post5" ], "DevDependencies": [ diff --git a/src/bot/translation/de.json b/src/bot/translation/de.json index 5ffe9dcf..7850782c 100644 --- a/src/bot/translation/de.json +++ b/src/bot/translation/de.json @@ -163,7 +163,7 @@ "auth": { "confirmation": { "subject": "E-Mail für {} {} bestätigen", - "message": "Öffne den Link um die E-Mail zu bestätigen:\n{}auth/forgot-password/{}" + "message": "Öffne den Link um die E-Mail zu bestätigen:\n{}auth/register/{}" }, "forgot_password": { "subject": "Passwort für {} {} zurücksetzen", diff --git a/src/bot_api/abc/auth_service_abc.py b/src/bot_api/abc/auth_service_abc.py index e4f611c6..28d13749 100644 --- a/src/bot_api/abc/auth_service_abc.py +++ b/src/bot_api/abc/auth_service_abc.py @@ -9,7 +9,6 @@ from bot_api.model.email_string_dto import EMailStringDTO from bot_api.model.reset_password_dto import ResetPasswordDTO from bot_api.model.token_dto import TokenDTO from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO -from bot_data.model.auth_user import AuthUser class AuthServiceABC(ABC): @@ -18,16 +17,16 @@ class AuthServiceABC(ABC): def __init__(self): pass @abstractmethod - async def get_all_auth_users_async(self) -> List[AuthUser]: pass + async def get_all_auth_users_async(self) -> List[AuthUserDTO]: pass @abstractmethod async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO: pass @abstractmethod - async def get_auth_user_by_email_async(self, email: str) -> AuthUser: pass + async def get_auth_user_by_email_async(self, email: str) -> AuthUserDTO: pass @abstractmethod - async def find_auth_user_by_email_async(self, email: str) -> AuthUser: pass + async def find_auth_user_by_email_async(self, email: str) -> AuthUserDTO: pass @abstractmethod async def add_auth_user_async(self, user_dto: AuthUserDTO) -> int: pass diff --git a/src/bot_api/api_module.py b/src/bot_api/api_module.py index ff1be35b..6474a3e5 100644 --- a/src/bot_api/api_module.py +++ b/src/bot_api/api_module.py @@ -10,7 +10,7 @@ from flask import Flask from bot_api.abc.auth_service_abc import AuthServiceABC from bot_api.api import Api from bot_api.api_thread import ApiThread -from bot_api.controller.api_controller import ApiController +from bot_api.controller.gui_controller import GuiController from bot_api.controller.auth_controller import AuthController from bot_api.service.auth_service import AuthService from bot_core.abc.module_abc import ModuleABC @@ -38,4 +38,4 @@ class ApiModule(ModuleABC): services.add_transient(AuthServiceABC, AuthService) services.add_transient(AuthController) - services.add_transient(ApiController) + services.add_transient(GuiController) diff --git a/src/bot_api/config/apisettings.edrafts-lapi.json b/src/bot_api/config/apisettings.edrafts-lapi.json index fb7c257c..d71694b2 100644 --- a/src/bot_api/config/apisettings.edrafts-lapi.json +++ b/src/bot_api/config/apisettings.edrafts-lapi.json @@ -7,7 +7,7 @@ "Authentication": { "SecretKey": "F3b5LDz+#Jvzg=W!@gsa%xsF", "Issuer": "http://localhost:5000", - "Audience": "http://localhost:5000", + "Audience": "http://localhost:4200", "TokenExpireTime": 1, "RefreshTokenExpireTime": 7 }, diff --git a/src/bot_api/controller/auth_controller.py b/src/bot_api/controller/auth_controller.py index adb6f3a2..c5e10d1c 100644 --- a/src/bot_api/controller/auth_controller.py +++ b/src/bot_api/controller/auth_controller.py @@ -40,21 +40,25 @@ class AuthController: @Route.get(f'{BasePath}/users') async def get_all_users(self) -> Response: - return jsonify(await self._auth_service.get_all_auth_users_async()) + result = await self._auth_service.get_all_auth_users_async() + return jsonify(result.select(lambda x: x.to_dict())) @Route.post(f'{BasePath}/users/get/filtered') async def get_filtered_users(self) -> Response: dto: AuthUserSelectCriteria = JSONProcessor.process(AuthUserSelectCriteria, request.get_json(force=True, silent=True)) result = await self._auth_service.get_filtered_auth_users_async(dto) + result.result = result.result.select(lambda x: x.to_dict()) return jsonify(result.to_dict()) @Route.get(f'{BasePath}/users/get/') async def get_user_from_email(self, email: str) -> Response: - return jsonify(await self._auth_service.get_auth_user_by_email_async(email)) + result = await self._auth_service.get_auth_user_by_email_async(email) + return jsonify(result.to_dict()) @Route.get(f'{BasePath}/users/find/') async def find_user_from_email(self, email: str) -> Response: - return jsonify(await self._auth_service.find_auth_user_by_email_async(email)) + result = await self._auth_service.find_auth_user_by_email_async(email) + return jsonify(result.to_dict()) @Route.post(f'{BasePath}/register') async def register(self): diff --git a/src/bot_api/controller/api_controller.py b/src/bot_api/controller/gui_controller.py similarity index 92% rename from src/bot_api/controller/api_controller.py rename to src/bot_api/controller/gui_controller.py index 1b7ae669..a2fa883a 100644 --- a/src/bot_api/controller/api_controller.py +++ b/src/bot_api/controller/gui_controller.py @@ -12,7 +12,8 @@ from bot_api.model.version_dto import VersionDTO from bot_api.route.route import Route -class ApiController: +class GuiController: + BasePath = f'/api/gui' def __init__( self, @@ -32,13 +33,13 @@ class ApiController: self._mail_settings = mail_settings self._mailer = mailer - @Route.route('/api/api-version') + @Route.get(f'{BasePath}/api-version') async def api_version(self): import bot_api version = bot_api.version_info return VersionDTO(version.major, version.minor, version.micro).to_dict() - @Route.route('/api/settings') + @Route.get(f'{BasePath}/settings') async def settings(self): # TODO: Authentication import bot_api @@ -59,7 +60,7 @@ class ApiController: self._mail_settings.user_name, ).to_dict() - @Route.route('/api/send-test-mail/') + @Route.get(f'{BasePath}/send-test-mail/') async def send_test_mail(self, email: str): # TODO: Authentication mail = EMail() diff --git a/src/bot_api/filter/auth_user_select_criteria.py b/src/bot_api/filter/auth_user_select_criteria.py index 497f5eaa..8ac7784e 100644 --- a/src/bot_api/filter/auth_user_select_criteria.py +++ b/src/bot_api/filter/auth_user_select_criteria.py @@ -13,7 +13,7 @@ class AuthUserSelectCriteria(SelectCriteriaABC): first_name: str, last_name: str, email: str, - auth_role=0 + auth_role: int ): SelectCriteriaABC.__init__(self, page_index, page_size, sort_direction, sort_column) diff --git a/src/bot_api/model/auth_user_dto.py b/src/bot_api/model/auth_user_dto.py index 19923890..4fb53394 100644 --- a/src/bot_api/model/auth_user_dto.py +++ b/src/bot_api/model/auth_user_dto.py @@ -1,8 +1,5 @@ -import traceback from typing import Optional -from cpl_core.console import Console - from bot_api.abc.dto_abc import DtoABC from bot_data.model.auth_role_enum import AuthRoleEnum @@ -14,7 +11,7 @@ class AuthUserDTO(DtoABC): id: int, first_name: str, last_name: str, - e_mail: str, + email: str, password: str, confirmation_id: Optional[str], auth_role: AuthRoleEnum, @@ -24,7 +21,7 @@ class AuthUserDTO(DtoABC): self._id = id self._first_name = first_name self._last_name = last_name - self._email = e_mail + self._email = email self._password = password self._is_confirmed = confirmation_id is None self._auth_role = auth_role @@ -75,11 +72,11 @@ class AuthUserDTO(DtoABC): @property def auth_role(self) -> AuthRoleEnum: - return self.auth_role + return self._auth_role @auth_role.setter def auth_role(self, value: AuthRoleEnum): - self.auth_role = value + self._auth_role = value def from_dict(self, values: dict): self._id = values['id'] @@ -98,5 +95,5 @@ class AuthUserDTO(DtoABC): 'email': self._email, 'password': self._password, 'isConfirmed': self._is_confirmed, - 'authRole': self._auth_role, + 'authRole': self._auth_role.value, } diff --git a/src/bot_api/service/auth_service.py b/src/bot_api/service/auth_service.py index 61b5cca6..07e75028 100644 --- a/src/bot_api/service/auth_service.py +++ b/src/bot_api/service/auth_service.py @@ -65,24 +65,35 @@ class AuthService(AuthServiceABC): @staticmethod def _is_email_valid(email: str) -> bool: - regex = '^[a-z0-9]+[\\._]?[a-z0-9]+[@]\\w+[.]\\w{2,3}$' - return bool(re.search(regex, email)) + if re.match(re.compile(r'^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$'), email) is not None: + return True + + return False def _generate_token(self, user: AuthUser) -> str: token = jwt.encode( payload={ 'user_id': user.id, 'email': user.email, - 'role': user.auth_role, + 'role': user.auth_role.value, 'exp': datetime.now(tz=timezone.utc) + timedelta(days=self._auth_settings.token_expire_time), 'iss': self._auth_settings.issuer, 'aud': self._auth_settings.audience }, - key=self._auth_settings.secret_key, + key=self._auth_settings.secret_key ) return token + def _decode_token(self, token: str) -> dict: + return jwt.decode( + token, + key=self._auth_settings.secret_key, + issuer=self._auth_settings.issuer, + audience=self._auth_settings.audience, + algorithms=['HS256'] + ) + def _create_and_save_refresh_token(self, user: AuthUser) -> str: token = str(uuid.uuid4()) user.refresh_token = token @@ -113,7 +124,7 @@ class AuthService(AuthServiceABC): mail.body = self._t.transform('api.auth.forgot_password.message').format(url, user.forgot_password_id) self._mailer.send_mail(mail) - async def get_all_auth_users_async(self) -> List[AuthUser]: + async def get_all_auth_users_async(self) -> List[AuthUserDTO]: result = self._auth_users.get_all_auth_users() \ .select(lambda x: AUT.to_dto(x)) return List(AuthUserDTO, result) @@ -127,10 +138,9 @@ class AuthService(AuthServiceABC): users.total_count ) - async def get_auth_user_by_email_async(self, email: str) -> AuthUser: + async def get_auth_user_by_email_async(self, email: str) -> AuthUserDTO: try: - user = self._auth_users.get_auth_user_by_email(email) - return user + return AUT.to_dto(self._auth_users.get_auth_user_by_email(email)) except Exception as e: self._logger.error(__name__, f'AuthUser not found', e) raise ServiceException(ServiceErrorCode.InvalidData, f'User not found {email}') @@ -139,7 +149,7 @@ class AuthService(AuthServiceABC): user = self._auth_users.find_auth_user_by_email(email) return AUT.to_dto(user) if user is not None else None - async def add_auth_user_async(self, user_dto: AuthUserDTO): + async def add_auth_user_async(self, user_dto: AuthUser): db_user = self._auth_users.find_auth_user_by_email(user_dto.email) if db_user is not None: raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') @@ -151,7 +161,7 @@ class AuthService(AuthServiceABC): try: user.confirmation_id = uuid.uuid4() - self._auth_users.update_auth_user(user) + self._auth_users.add_auth_user(user) self._send_confirmation_id_to_user(user) self._db.save_changes() self._logger.info(__name__, f'Added auth user with E-Mail: {user_dto.email}') @@ -273,7 +283,7 @@ class AuthService(AuthServiceABC): self._logger.error(__name__, f'Cannot delete user', e) raise ServiceException(ServiceErrorCode.UnableToDelete, f'Cannot delete user by mail {email}') - async def delete_auth_user_async(self, user_dto: AuthUserDTO): + async def delete_auth_user_async(self, user_dto: AuthUser): try: self._auth_users.delete_auth_user(AUT.to_db(user_dto)) self._db.save_changes() @@ -281,7 +291,7 @@ class AuthService(AuthServiceABC): self._logger.error(__name__, f'Cannot delete user', e) raise ServiceException(ServiceErrorCode.UnableToDelete, f'Cannot delete user by mail {user_dto.email}') - async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: + async def login_async(self, user_dto: AuthUser) -> TokenDTO: if user_dto is None: raise ServiceException(ServiceErrorCode.InvalidData, 'User not set') @@ -305,12 +315,12 @@ class AuthService(AuthServiceABC): if token_dto is None: raise ServiceException(ServiceErrorCode.InvalidData, f'Token not set') - token = jwt.decode(token_dto.token, key=self._auth_settings.secret_key) + token = self._decode_token(token_dto.token) if token is None or 'email' not in token: raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') try: - user = self._auth_users.get_auth_user_by_email(token) + user = self._auth_users.get_auth_user_by_email(token['email']) if user is None or user.refresh_token != token_dto.refresh_token or user.refresh_token_expire_time <= datetime.now(): raise ServiceException(ServiceErrorCode.InvalidData, 'Token expired') @@ -323,9 +333,9 @@ class AuthService(AuthServiceABC): if token_dto is None or token_dto.token is None or token_dto.refresh_token is None: raise ServiceException(ServiceErrorCode.InvalidData, 'Token not set') - token = jwt.decode(token_dto.token, key=self._auth_settings.secret_key) + token = self._decode_token(token_dto.token) try: - user = self._auth_users.get_auth_user_by_email(token) + user = self._auth_users.get_auth_user_by_email(token['email']) if user is None or user.refresh_token != token_dto.refresh_token or user.refresh_token_expire_time <= datetime.now(): raise ServiceException(ServiceErrorCode.InvalidData, 'Token expired') diff --git a/src/bot_api/transformer/auth_user_transformer.py b/src/bot_api/transformer/auth_user_transformer.py index 50258b01..b324445b 100644 --- a/src/bot_api/transformer/auth_user_transformer.py +++ b/src/bot_api/transformer/auth_user_transformer.py @@ -1,12 +1,15 @@ +from datetime import datetime, timezone + from bot_api.abc.auth_user_transformer_abc import AuthUserTransformerABC from bot_api.model.auth_user_dto import AuthUserDTO +from bot_data.model.auth_role_enum import AuthRoleEnum from bot_data.model.auth_user import AuthUser class AuthUserTransformer(AuthUserTransformerABC): @staticmethod - def to_db(dto: AuthUserDTO) -> AuthUser: + def to_db(dto: AuthUser) -> AuthUser: return AuthUser( dto.first_name, dto.last_name, @@ -15,9 +18,9 @@ class AuthUserTransformer(AuthUserTransformerABC): None, None, None, - None, - dto.auth_role, - id=dto.id + datetime.now(tz=timezone.utc), + AuthRoleEnum.normal if dto.auth_role is None else dto.auth_role, + id=0 if dto.id is None else dto.id ) @staticmethod @@ -28,6 +31,6 @@ class AuthUserTransformer(AuthUserTransformerABC): db.last_name, db.email, db.password, - db.confirmation_id is None, + db.confirmation_id, db.auth_role ) diff --git a/src/bot_data/migration/api_migration.py b/src/bot_data/migration/api_migration.py index 61f9d156..80754c88 100644 --- a/src/bot_data/migration/api_migration.py +++ b/src/bot_data/migration/api_migration.py @@ -18,18 +18,19 @@ class ApiMigration(MigrationABC): self._cursor.execute( str(f""" CREATE TABLE IF NOT EXISTS `AuthUsers` ( - `Id` bigint NOT NULL, - `FirstName` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, - `LastName` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, - `EMail` varchar(255) DEFAULT NULL, - `Password` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, - `RefreshToken` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci, - `ConfirmationId` varchar(255) DEFAULT NULL, - `ForgotPasswordId` varchar(255) DEFAULT NULL, - `RefreshTokenExpiryTime` datetime(6) NOT NULL, - `AuthRole` int NOT NULL DEFAULT '0', - `CreatedOn` datetime(6) NOT NULL, - `LastModifiedOn` datetime(6) NOT NULL + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `FirstName` VARCHAR(255), + `LastName` VARCHAR(255), + `EMail` VARCHAR(255), + `Password` VARCHAR(255), + `RefreshToken` VARCHAR(255), + `ConfirmationId` VARCHAR(255) DEFAULT NULL, + `ForgotPasswordId` VARCHAR(255) DEFAULT NULL, + `RefreshTokenExpiryTime` DATETIME(6) NOT NULL, + `AuthRole` INT NOT NULL DEFAULT '0', + `CreatedOn` DATETIME(6) NOT NULL, + `LastModifiedOn` DATETIME(6) NOT NULL, + PRIMARY KEY(`Id`) ) """) ) diff --git a/src/bot_data/model/auth_role_enum.py b/src/bot_data/model/auth_role_enum.py index 8b032325..e1526136 100644 --- a/src/bot_data/model/auth_role_enum.py +++ b/src/bot_data/model/auth_role_enum.py @@ -3,5 +3,5 @@ from enum import Enum class AuthRoleEnum(Enum): - Normal = 0 - Admin = 1 + normal = 0 + admin = 1 diff --git a/src/bot_data/model/auth_user.py b/src/bot_data/model/auth_user.py index 57cdde0a..26ba09a8 100644 --- a/src/bot_data/model/auth_user.py +++ b/src/bot_data/model/auth_user.py @@ -17,7 +17,7 @@ class AuthUser(TableABC): refresh_token: Optional[str], confirmation_id: Optional[str], forgot_password_id: Optional[str], - refresh_token_expire_time: Optional[datetime], + refresh_token_expire_time: datetime, auth_role: AuthRoleEnum, created_at: datetime = None, modified_at: datetime = None, @@ -100,11 +100,11 @@ class AuthUser(TableABC): self._forgot_password_id = value @property - def refresh_token_expire_time(self) -> Optional[datetime]: + def refresh_token_expire_time(self) -> datetime: return self._refresh_token_expire_time @refresh_token_expire_time.setter - def refresh_token_expire_time(self, value: Optional[datetime]): + def refresh_token_expire_time(self, value: datetime): self._refresh_token_expire_time = value @property @@ -143,7 +143,7 @@ class AuthUser(TableABC): """) @staticmethod - def get_select_by_forgot_password_i_string(id: str) -> str: + def get_select_by_forgot_password_id_string(id: str) -> str: return str(f""" SELECT * FROM `AuthUsers` WHERE `ForgotPasswordId` = '{id}'; @@ -172,8 +172,8 @@ class AuthUser(TableABC): '{self._email}', '{self._password}', '{self._refresh_token}', - '{self._confirmation_id}', - '{self._forgot_password_id}', + '{"NULL" if self._confirmation_id is None else self._confirmation_id}', + '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', '{self._refresh_token_expire_time}', {self._auth_role_id.value}, '{self._created_at}', @@ -190,11 +190,11 @@ class AuthUser(TableABC): `EMail` = '{self._email}', `Password` = '{self._password}', `RefreshToken` = '{self._refresh_token}', - `ConfirmationId` = '{self._confirmation_id}', - `ForgotPasswordId` = '{self._forgot_password_id}', + `ConfirmationId` = '{"NULL" if self._confirmation_id is None else self._confirmation_id}', + `ForgotPasswordId` = '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', `RefreshTokenExpiryTime` = '{self._refresh_token_expire_time}', - `AutoRole` = {self._auth_role_id.value}, - `LastModifiedAt` = '{self._modified_at}' + `AuthRole` = {self._auth_role_id.value}, + `LastModifiedOn` = '{self._modified_at}' WHERE `AuthUsers`.`Id` = {self._auth_user_id}; """) diff --git a/src/bot_data/service/auth_user_repository_service.py b/src/bot_data/service/auth_user_repository_service.py index 2096113e..4918b269 100644 --- a/src/bot_data/service/auth_user_repository_service.py +++ b/src/bot_data/service/auth_user_repository_service.py @@ -20,18 +20,24 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): AuthUserRepositoryABC.__init__(self) @staticmethod - def _user_from_result(result: tuple) -> AuthUser: + def _get_value_from_result(value: any) -> Optional[any]: + if isinstance(value, str) and 'NULL' in value: + return None + + return value + + def _user_from_result(self, result: tuple) -> AuthUser: return AuthUser( - result[1], - result[2], - result[3], - result[4], - result[5], - result[6], - result[7], - result[8], - AuthRoleEnum(result[9]), - id=result[0] + self._get_value_from_result(result[1]), + self._get_value_from_result(result[2]), + self._get_value_from_result(result[3]), + self._get_value_from_result(result[4]), + self._get_value_from_result(result[5]), + self._get_value_from_result(result[6]), + self._get_value_from_result(result[7]), + self._get_value_from_result(result[8]), + AuthRoleEnum(self._get_value_from_result(result[9])), + id=self._get_value_from_result(result[0]) ) def get_all_auth_users(self) -> List[AuthUser]: @@ -47,7 +53,6 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): def get_filtered_auth_users(self, criteria: AuthUserSelectCriteria) -> FilteredResult: users = self.get_all_auth_users() self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_all_string()}') - query = users if criteria.first_name is not None and criteria.first_name != '': @@ -70,9 +75,9 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): else: query = query.order_by(lambda x: getattr(x, criteria.sort_column)) - skip = criteria.page_size * criteria.page_index result = FilteredResult() result.total_count = query.count() + skip = criteria.page_size * criteria.page_index result.result = query.skip(skip).take(criteria.page_size) return result @@ -93,8 +98,8 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): return self._user_from_result(result) def find_auth_user_by_confirmation_id(self, id: str) -> Optional[AuthUser]: - self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_by_email_string(id)}') - result = self._context.select(AuthUser.get_select_by_email_string(id)) + self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_by_confirmation_id_string(id)}') + result = self._context.select(AuthUser.get_select_by_confirmation_id_string(id)) if result is None or len(result) == 0: return None @@ -103,8 +108,8 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): return self._user_from_result(result) def find_auth_user_by_forgot_password_id(self, id: str) -> Optional[AuthUser]: - self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_by_email_string(id)}') - result = self._context.select(AuthUser.get_select_by_email_string(id)) + self._logger.trace(__name__, f'Send SQL command: {AuthUser.get_select_by_forgot_password_id_string(id)}') + result = self._context.select(AuthUser.get_select_by_forgot_password_id_string(id)) if result is None or len(result) == 0: return None From 21fcfaaa7f5e9b0e0742f235f3ae85c48dec3420 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 15 Oct 2022 17:07:22 +0200 Subject: [PATCH 018/275] Fixed some random stuff #70 --- src/bot/bot.json | 2 +- src/bot/config/appsettings.edrafts-lapi.json | 2 +- src/bot_api/api.py | 3 ++- src/bot_data/db_context.py | 2 -- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/bot/bot.json b/src/bot/bot.json index df826bb7..89afddf9 100644 --- a/src/bot/bot.json +++ b/src/bot/bot.json @@ -18,7 +18,7 @@ "Dependencies": [ "cpl-core==2022.10.0.post6", "cpl-translation==2022.10.0.post1", - "cpl-query==2022.10.0.post1", + "cpl-query==2022.10.0.post2", "cpl-discord==2022.10.0.post5" ], "DevDependencies": [ diff --git a/src/bot/config/appsettings.edrafts-lapi.json b/src/bot/config/appsettings.edrafts-lapi.json index 2e9e52a3..f0b9f1d1 100644 --- a/src/bot/config/appsettings.edrafts-lapi.json +++ b/src/bot/config/appsettings.edrafts-lapi.json @@ -27,7 +27,7 @@ "Database": { "Path": "logs/", "Filename": "database.log", - "ConsoleLogLevel": "DEBUG", + "ConsoleLogLevel": "TRACE", "FileLogLevel": "TRACE" }, "Message": { diff --git a/src/bot_api/api.py b/src/bot_api/api.py index 6a08c9a6..7930b861 100644 --- a/src/bot_api/api.py +++ b/src/bot_api/api.py @@ -77,4 +77,5 @@ class Api(Flask): self._logger.info(__name__, f'Starting API {self._apt_settings.host}:{self._apt_settings.port}') self._register_routes() from waitress import serve - serve(self, host=self._apt_settings.host, port=self._apt_settings.port) + # https://docs.pylonsproject.org/projects/waitress/en/stable/arguments.html + serve(self, host=self._apt_settings.host, port=self._apt_settings.port, threads=10, connection_limit=1000, channel_timeout=10) diff --git a/src/bot_data/db_context.py b/src/bot_data/db_context.py index fb0df290..0f867228 100644 --- a/src/bot_data/db_context.py +++ b/src/bot_data/db_context.py @@ -16,8 +16,6 @@ class DBContext(DatabaseContext): try: self._logger.debug(__name__, "Connecting to database") self._db.connect(database_settings) - - self.save_changes() self._logger.info(__name__, "Connected to database") except Exception as e: self._logger.fatal(__name__, "Connecting to database failed", e) From 8e56ff6a8e21a1e888b0345a8a170817dfcc2407 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 15 Oct 2022 17:07:46 +0200 Subject: [PATCH 019/275] Set log level #70 --- src/bot/config/appsettings.edrafts-lapi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot/config/appsettings.edrafts-lapi.json b/src/bot/config/appsettings.edrafts-lapi.json index f0b9f1d1..2e9e52a3 100644 --- a/src/bot/config/appsettings.edrafts-lapi.json +++ b/src/bot/config/appsettings.edrafts-lapi.json @@ -27,7 +27,7 @@ "Database": { "Path": "logs/", "Filename": "database.log", - "ConsoleLogLevel": "TRACE", + "ConsoleLogLevel": "DEBUG", "FileLogLevel": "TRACE" }, "Message": { From 9da95f4dfb6a21efa1fda35e75c11cda8105a114 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 15 Oct 2022 17:25:49 +0200 Subject: [PATCH 020/275] Fixed update user #70 --- src/bot_api/json_processor.py | 11 ++++++-- src/bot_api/model/update_auth_user_dto.py | 8 +++--- src/bot_api/service/auth_service.py | 33 ++++++++++++----------- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/bot_api/json_processor.py b/src/bot_api/json_processor.py index e629258e..a0ea0ac1 100644 --- a/src/bot_api/json_processor.py +++ b/src/bot_api/json_processor.py @@ -16,12 +16,19 @@ class JSONProcessor: continue name = String.convert_to_camel_case(parameter.name) + name = name.replace('Dto', 'DTO') name_first_lower = String.first_to_lower(name) if name in values or name_first_lower in values: + value = '' if name in values: - args.append(values[name]) + value = values[name] else: - args.append(values[name_first_lower]) + value = values[name_first_lower] + + if isinstance(value, dict): + value = JSONProcessor.process(parameter.annotation, value) + + args.append(value) elif parameter.default != Parameter.empty: args.append(parameter.default) diff --git a/src/bot_api/model/update_auth_user_dto.py b/src/bot_api/model/update_auth_user_dto.py index 682f0122..9caa9473 100644 --- a/src/bot_api/model/update_auth_user_dto.py +++ b/src/bot_api/model/update_auth_user_dto.py @@ -10,14 +10,14 @@ class UpdateAuthUserDTO(DtoABC): def __init__( self, - auth_user: AuthUserDTO, - new_auth_user: AuthUserDTO, + auth_user_dto: AuthUserDTO, + new_auth_user_dto: AuthUserDTO, change_password=False ): DtoABC.__init__(self) - self._auth_user = auth_user - self._new_auth_user = new_auth_user + self._auth_user = auth_user_dto + self._new_auth_user = new_auth_user_dto self._change_password = change_password @property diff --git a/src/bot_api/service/auth_service.py b/src/bot_api/service/auth_service.py index 07e75028..ede6f15f 100644 --- a/src/bot_api/service/auth_service.py +++ b/src/bot_api/service/auth_service.py @@ -191,12 +191,12 @@ class AuthService(AuthServiceABC): # update first name if update_user_dto.new_auth_user.first_name is not None and update_user_dto.auth_user.first_name != update_user_dto.new_auth_user.first_name: - user.FirstName = update_user_dto.new_auth_user.first_name + user.first_name = update_user_dto.new_auth_user.first_name # update last name if update_user_dto.new_auth_user.last_name is not None and update_user_dto.new_auth_user.last_name != '' and \ update_user_dto.auth_user.last_name != update_user_dto.new_auth_user.last_name: - user.LastName = update_user_dto.new_auth_user.last_name + user.last_name = update_user_dto.new_auth_user.last_name # update E-Mail if update_user_dto.new_auth_user.email is not None and update_user_dto.new_auth_user.email != '' and update_user_dto.auth_user.email != update_user_dto.new_auth_user.email: @@ -208,21 +208,22 @@ class AuthService(AuthServiceABC): is_existing_password_set = False is_new_password_set = False # hash passwords in DTOs - if update_user_dto.auth_user.Password is not None and update_user_dto.auth_user.Password != '': + if update_user_dto.auth_user.password is not None and update_user_dto.auth_user.password != '': is_existing_password_set = True - update_user_dto.auth_user.Password = self._hash_sha256(update_user_dto.auth_user.Password) + update_user_dto.auth_user.password = self._hash_sha256(update_user_dto.auth_user.password) - if update_user_dto.auth_user.Password != user.Password: + if update_user_dto.auth_user.password != user.password: raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password') - if update_user_dto.new_auth_user.Password is not None and update_user_dto.new_auth_user.Password != '': + if update_user_dto.new_auth_user.password is not None and update_user_dto.new_auth_user.password != '': is_new_password_set = True - update_user_dto.new_auth_user.Password = self._hash_sha256(update_user_dto.new_auth_user.Password) + update_user_dto.new_auth_user.password = self._hash_sha256(update_user_dto.new_auth_user.password) # update password - if is_existing_password_set and is_new_password_set and update_user_dto.auth_user.Password != update_user_dto.new_auth_user.Password: - user.Password = update_user_dto.new_auth_user.Password + if is_existing_password_set and is_new_password_set and update_user_dto.auth_user.password != update_user_dto.new_auth_user.password: + user.password = update_user_dto.new_auth_user.password + self._auth_users.update_auth_user(user) self._db.save_changes() async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO): @@ -242,31 +243,31 @@ class AuthService(AuthServiceABC): if user is None: raise ServiceException(ServiceErrorCode.InvalidUser, 'User not found') - if user.ConfirmationId is not None and update_user_dto.new_auth_user.is_confirmed: - user.ConfirmationId = None - elif user.ConfirmationId is None and not update_user_dto.new_auth_user.is_confirmed: + if user.confirmation_id is not None and update_user_dto.new_auth_user.is_confirmed: + user.confirmation_id = None + elif user.confirmation_id is None and not update_user_dto.new_auth_user.is_confirmed: user.confirmation_id = uuid.uuid4() # else # raise ServiceException(ServiceErrorCode.InvalidUser, 'E-Mail not confirmed') # update first name if update_user_dto.new_auth_user.first_name is not None and update_user_dto.auth_user.first_name != update_user_dto.new_auth_user.first_name: - user.FirstName = update_user_dto.new_auth_user.first_name + user.first_name = update_user_dto.new_auth_user.first_name # update last name if update_user_dto.new_auth_user.last_name is not None and update_user_dto.new_auth_user.last_name != '' and update_user_dto.auth_user.last_name != update_user_dto.new_auth_user.last_name: - user.LastName = update_user_dto.new_auth_user.last_name + user.last_name = update_user_dto.new_auth_user.last_name # update E-Mail if update_user_dto.new_auth_user.email is not None and update_user_dto.new_auth_user.email != '' and update_user_dto.auth_user.email != update_user_dto.new_auth_user.email: user_by_new_e_mail = self._auth_users.find_auth_user_by_email(update_user_dto.new_auth_user.email) if user_by_new_e_mail is not None: raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') - user.EMail = update_user_dto.new_auth_user.email + user.email = update_user_dto.new_auth_user.email # update password if update_user_dto.change_password and update_user_dto.auth_user.password != update_user_dto.new_auth_user.password: - user.Password = self._hash_sha256(update_user_dto.new_auth_user.password) + user.password = self._hash_sha256(update_user_dto.new_auth_user.password) # update role if user.auth_role == update_user_dto.auth_user.auth_role and user.auth_role != update_user_dto.new_auth_user.auth_role: From 029b46d7de14411cd6d3dbe43a1a46d8473a2e48 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 15 Oct 2022 17:34:16 +0200 Subject: [PATCH 021/275] Build #70 --- src/bot_api/exception/__init__.py | 26 ++++++++++++++++++++++++++ src/bot_api/transformer/__init__.py | 26 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/bot_api/exception/__init__.py b/src/bot_api/exception/__init__.py index e69de29b..b37a52c4 100644 --- a/src/bot_api/exception/__init__.py +++ b/src/bot_api/exception/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'bot_api.exception' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.2.3' + +from collections import namedtuple + + +# imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='2', micro='3') diff --git a/src/bot_api/transformer/__init__.py b/src/bot_api/transformer/__init__.py index e69de29b..4c614cb9 100644 --- a/src/bot_api/transformer/__init__.py +++ b/src/bot_api/transformer/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'bot_api.transformer' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.2.3' + +from collections import namedtuple + + +# imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='2', micro='3') From ead3f69a69b5de9da222050d523dafc9b6cc36cb Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 15 Oct 2022 23:17:45 +0200 Subject: [PATCH 022/275] Moved bot to kdb-bot #70 --- cpl-workspace.json => kdb-bot/cpl-workspace.json | 0 docker-compose.yml => kdb-bot/docker-compose.yml | 0 dockerfile => kdb-bot/dockerfile | 0 {src => kdb-bot/src}/bot/__init__.py | 0 {src => kdb-bot/src}/bot/application.py | 0 {src => kdb-bot/src}/bot/bot | 0 {src => kdb-bot/src}/bot/bot.json | 0 {src => kdb-bot/src}/bot/config/appsettings.PC-Nick.json | 0 {src => kdb-bot/src}/bot/config/appsettings.development.json | 0 {src => kdb-bot/src}/bot/config/appsettings.edrafts-lapi.json | 0 .../src}/bot/config/appsettings.edrafts-pc-ubuntu.json | 0 {src => kdb-bot/src}/bot/config/appsettings.example.json | 0 {src => kdb-bot/src}/bot/config/appsettings.json | 0 {src => kdb-bot/src}/bot/config/appsettings.production.json | 0 {src => kdb-bot/src}/bot/config/appsettings.staging.json | 0 {src => kdb-bot/src}/bot/config/feature-flags.json | 0 {src => kdb-bot/src}/bot/main.py | 0 {src => kdb-bot/src}/bot/module_list.py | 0 {src => kdb-bot/src}/bot/startup.py | 0 {src => kdb-bot/src}/bot/startup_discord_extension.py | 0 {src => kdb-bot/src}/bot/startup_migration_extension.py | 0 {src => kdb-bot/src}/bot/startup_module_extension.py | 0 {src => kdb-bot/src}/bot/startup_settings_extension.py | 0 {src => kdb-bot/src}/bot/translation/de.json | 0 {src => kdb-bot/src}/bot_api/__init__.py | 0 {src => kdb-bot/src}/bot_api/abc/__init__.py | 0 {src => kdb-bot/src}/bot_api/abc/auth_service_abc.py | 0 {src => kdb-bot/src}/bot_api/abc/auth_user_transformer_abc.py | 0 {src => kdb-bot/src}/bot_api/abc/dto_abc.py | 0 {src => kdb-bot/src}/bot_api/abc/select_criteria_abc.py | 0 {src => kdb-bot/src}/bot_api/api.py | 0 {src => kdb-bot/src}/bot_api/api_module.py | 0 {src => kdb-bot/src}/bot_api/api_thread.py | 0 {src => kdb-bot/src}/bot_api/bot-api.json | 0 {src => kdb-bot/src}/bot_api/config/apisettings.development.json | 0 {src => kdb-bot/src}/bot_api/config/apisettings.edrafts-lapi.json | 0 .../src}/bot_api/config/apisettings.edrafts-pc-ubuntu.json | 0 {src => kdb-bot/src}/bot_api/config/apisettings.json | 0 {src => kdb-bot/src}/bot_api/config/apisettings.production.json | 0 {src => kdb-bot/src}/bot_api/config/apisettings.staging.json | 0 {src => kdb-bot/src}/bot_api/config/appsettings.PC-Nick.json | 0 {src => kdb-bot/src}/bot_api/configuration/__init__.py | 0 {src => kdb-bot/src}/bot_api/configuration/api_settings.py | 0 .../src}/bot_api/configuration/authentication_settings.py | 0 {src => kdb-bot/src}/bot_api/configuration/frontend_settings.py | 0 {src => kdb-bot/src}/bot_api/configuration/version_settings.py | 0 {src => kdb-bot/src}/bot_api/controller/__init__.py | 0 {src => kdb-bot/src}/bot_api/controller/auth_controller.py | 0 {src => kdb-bot/src}/bot_api/controller/gui_controller.py | 0 {src => kdb-bot/src}/bot_api/exception/__init__.py | 0 {src => kdb-bot/src}/bot_api/exception/service_error_code_enum.py | 0 {src => kdb-bot/src}/bot_api/exception/service_exception.py | 0 {src => kdb-bot/src}/bot_api/filter/__init__.py | 0 {src => kdb-bot/src}/bot_api/filter/auth_user_select_criteria.py | 0 {src => kdb-bot/src}/bot_api/json_processor.py | 0 {src => kdb-bot/src}/bot_api/logging/__init__.py | 0 {src => kdb-bot/src}/bot_api/logging/api_logger.py | 0 {src => kdb-bot/src}/bot_api/model/__init__.py | 0 {src => kdb-bot/src}/bot_api/model/auth_user_dto.py | 0 .../src}/bot_api/model/auth_user_filtered_result_dto.py | 0 {src => kdb-bot/src}/bot_api/model/email_string_dto.py | 0 {src => kdb-bot/src}/bot_api/model/error_dto.py | 0 {src => kdb-bot/src}/bot_api/model/reset_password_dto.py | 0 {src => kdb-bot/src}/bot_api/model/settings_dto.py | 0 {src => kdb-bot/src}/bot_api/model/token_dto.py | 0 {src => kdb-bot/src}/bot_api/model/update_auth_user_dto.py | 0 {src => kdb-bot/src}/bot_api/model/version_dto.py | 0 {src => kdb-bot/src}/bot_api/route/__init__.py | 0 {src => kdb-bot/src}/bot_api/route/route.py | 0 {src => kdb-bot/src}/bot_api/service/__init__.py | 0 {src => kdb-bot/src}/bot_api/service/auth_service.py | 0 {src => kdb-bot/src}/bot_api/transformer/__init__.py | 0 {src => kdb-bot/src}/bot_api/transformer/auth_user_transformer.py | 0 {src => kdb-bot/src}/bot_core/__init__.py | 0 {src => kdb-bot/src}/bot_core/abc/__init__.py | 0 {src => kdb-bot/src}/bot_core/abc/client_utils_service_abc.py | 0 {src => kdb-bot/src}/bot_core/abc/custom_file_logger_abc.py | 0 {src => kdb-bot/src}/bot_core/abc/message_service_abc.py | 0 {src => kdb-bot/src}/bot_core/abc/module_abc.py | 0 {src => kdb-bot/src}/bot_core/bot-core.json | 0 {src => kdb-bot/src}/bot_core/configuration/__init__.py | 0 .../src}/bot_core/configuration/bot_logging_settings.py | 0 {src => kdb-bot/src}/bot_core/configuration/bot_settings.py | 0 {src => kdb-bot/src}/bot_core/configuration/feature_flags_enum.py | 0 .../src}/bot_core/configuration/feature_flags_settings.py | 0 .../src}/bot_core/configuration/file_logging_settings.py | 0 {src => kdb-bot/src}/bot_core/configuration/server_settings.py | 0 {src => kdb-bot/src}/bot_core/core_extension/__init__.py | 0 .../src}/bot_core/core_extension/core_extension_module.py | 0 .../src}/bot_core/core_extension/core_extension_on_ready_event.py | 0 {src => kdb-bot/src}/bot_core/core_module.py | 0 {src => kdb-bot/src}/bot_core/events/__init__.py | 0 {src => kdb-bot/src}/bot_core/events/core_on_ready_event.py | 0 {src => kdb-bot/src}/bot_core/helper/__init__.py | 0 {src => kdb-bot/src}/bot_core/helper/log_message_helper.py | 0 {src => kdb-bot/src}/bot_core/logging/__init__.py | 0 {src => kdb-bot/src}/bot_core/logging/command_logger.py | 0 {src => kdb-bot/src}/bot_core/logging/database_logger.py | 0 {src => kdb-bot/src}/bot_core/logging/message_logger.py | 0 {src => kdb-bot/src}/bot_core/pipes/__init__.py | 0 {src => kdb-bot/src}/bot_core/pipes/date_time_offset_pipe.py | 0 {src => kdb-bot/src}/bot_core/service/__init__.py | 0 {src => kdb-bot/src}/bot_core/service/client_utils_service.py | 0 {src => kdb-bot/src}/bot_core/service/message_service.py | 0 {src => kdb-bot/src}/bot_data/__init__.py | 0 {src => kdb-bot/src}/bot_data/abc/__init__.py | 0 {src => kdb-bot/src}/bot_data/abc/auth_user_repository_abc.py | 0 {src => kdb-bot/src}/bot_data/abc/auto_role_repository_abc.py | 0 {src => kdb-bot/src}/bot_data/abc/client_repository_abc.py | 0 {src => kdb-bot/src}/bot_data/abc/known_user_repository_abc.py | 0 {src => kdb-bot/src}/bot_data/abc/migration_abc.py | 0 {src => kdb-bot/src}/bot_data/abc/server_repository_abc.py | 0 .../src}/bot_data/abc/user_joined_server_repository_abc.py | 0 .../src}/bot_data/abc/user_joined_voice_channel_abc.py | 0 {src => kdb-bot/src}/bot_data/abc/user_repository_abc.py | 0 {src => kdb-bot/src}/bot_data/bot-data.json | 0 {src => kdb-bot/src}/bot_data/data_module.py | 0 {src => kdb-bot/src}/bot_data/db_context.py | 0 {src => kdb-bot/src}/bot_data/filtered_result.py | 0 {src => kdb-bot/src}/bot_data/migration/__init__.py | 0 {src => kdb-bot/src}/bot_data/migration/api_migration.py | 0 {src => kdb-bot/src}/bot_data/migration/auto_role_migration.py | 0 {src => kdb-bot/src}/bot_data/migration/initial_migration.py | 0 {src => kdb-bot/src}/bot_data/model/__init__.py | 0 {src => kdb-bot/src}/bot_data/model/auth_role_enum.py | 0 {src => kdb-bot/src}/bot_data/model/auth_user.py | 0 {src => kdb-bot/src}/bot_data/model/auto_role.py | 0 {src => kdb-bot/src}/bot_data/model/auto_role_rule.py | 0 {src => kdb-bot/src}/bot_data/model/client.py | 0 {src => kdb-bot/src}/bot_data/model/known_user.py | 0 {src => kdb-bot/src}/bot_data/model/migration_history.py | 0 {src => kdb-bot/src}/bot_data/model/server.py | 0 {src => kdb-bot/src}/bot_data/model/user.py | 0 {src => kdb-bot/src}/bot_data/model/user_joined_server.py | 0 {src => kdb-bot/src}/bot_data/model/user_joined_voice_channel.py | 0 {src => kdb-bot/src}/bot_data/service/__init__.py | 0 .../src}/bot_data/service/auth_user_repository_service.py | 0 .../src}/bot_data/service/auto_role_repository_service.py | 0 .../src}/bot_data/service/client_repository_service.py | 0 .../src}/bot_data/service/known_user_repository_service.py | 0 {src => kdb-bot/src}/bot_data/service/migration_service.py | 0 .../src}/bot_data/service/server_repository_service.py | 0 .../bot_data/service/user_joined_server_repository_service.py | 0 .../src}/bot_data/service/user_joined_voice_channel_service.py | 0 {src => kdb-bot/src}/bot_data/service/user_repository_service.py | 0 {src => kdb-bot/src}/modules/__init__.py | 0 {src => kdb-bot/src}/modules/admin/__init__.py | 0 {src => kdb-bot/src}/modules/admin/admin.json | 0 {src => kdb-bot/src}/modules/admin/admin_module.py | 0 {src => kdb-bot/src}/modules/admin/command/__init__.py | 0 {src => kdb-bot/src}/modules/admin/command/restart_command.py | 0 {src => kdb-bot/src}/modules/admin/command/shutdown_command.py | 0 {src => kdb-bot/src}/modules/auto_role/__init__.py | 0 {src => kdb-bot/src}/modules/auto_role/auto-role.json | 0 {src => kdb-bot/src}/modules/auto_role/auto_role_module.py | 0 {src => kdb-bot/src}/modules/auto_role/command/__init__.py | 0 {src => kdb-bot/src}/modules/auto_role/command/auto_role_group.py | 0 {src => kdb-bot/src}/modules/auto_role/events/__init__.py | 0 .../modules/auto_role/events/auto_role_on_raw_reaction_add.py | 0 .../modules/auto_role/events/auto_role_on_raw_reaction_remove.py | 0 {src => kdb-bot/src}/modules/auto_role/helper/__init__.py | 0 {src => kdb-bot/src}/modules/auto_role/helper/reaction_handler.py | 0 {src => kdb-bot/src}/modules/base/__init__.py | 0 {src => kdb-bot/src}/modules/base/abc/__init__.py | 0 {src => kdb-bot/src}/modules/base/abc/base_helper_abc.py | 0 {src => kdb-bot/src}/modules/base/base.json | 0 {src => kdb-bot/src}/modules/base/base_module.py | 0 {src => kdb-bot/src}/modules/base/command/__init__.py | 0 {src => kdb-bot/src}/modules/base/command/afk_command.py | 0 {src => kdb-bot/src}/modules/base/command/help_command.py | 0 {src => kdb-bot/src}/modules/base/command/info_command.py | 0 {src => kdb-bot/src}/modules/base/command/ping_command.py | 0 {src => kdb-bot/src}/modules/base/configuration/__init__.py | 0 .../src}/modules/base/configuration/base_server_settings.py | 0 {src => kdb-bot/src}/modules/base/configuration/base_settings.py | 0 {src => kdb-bot/src}/modules/base/events/__init__.py | 0 .../src}/modules/base/events/base_on_command_error_event.py | 0 {src => kdb-bot/src}/modules/base/events/base_on_command_event.py | 0 .../src}/modules/base/events/base_on_member_join_event.py | 0 .../src}/modules/base/events/base_on_member_remove_event.py | 0 {src => kdb-bot/src}/modules/base/events/base_on_message_event.py | 0 .../src}/modules/base/events/base_on_voice_state_update_event.py | 0 {src => kdb-bot/src}/modules/base/service/__init__.py | 0 {src => kdb-bot/src}/modules/base/service/base_helper_service.py | 0 {src => kdb-bot/src}/modules/boot_log/__init__.py | 0 {src => kdb-bot/src}/modules/boot_log/boot-log.json | 0 {src => kdb-bot/src}/modules/boot_log/boot_log_extension.py | 0 {src => kdb-bot/src}/modules/boot_log/boot_log_module.py | 0 {src => kdb-bot/src}/modules/boot_log/boot_log_on_ready_event.py | 0 {src => kdb-bot/src}/modules/boot_log/configuration/__init__.py | 0 .../modules/boot_log/configuration/boot_log_server_settings.py | 0 .../src}/modules/boot_log/configuration/boot_log_settings.py | 0 {src => kdb-bot/src}/modules/database/__init__.py | 0 {src => kdb-bot/src}/modules/database/database.json | 0 {src => kdb-bot/src}/modules/database/database_extension.py | 0 {src => kdb-bot/src}/modules/database/database_module.py | 0 {src => kdb-bot/src}/modules/database/database_on_ready_event.py | 0 {src => kdb-bot/src}/modules/moderator/__init__.py | 0 {src => kdb-bot/src}/modules/moderator/command/__init__.py | 0 {src => kdb-bot/src}/modules/moderator/command/purge_command.py | 0 {src => kdb-bot/src}/modules/moderator/command/user_group.py | 0 {src => kdb-bot/src}/modules/moderator/moderator.json | 0 {src => kdb-bot/src}/modules/moderator/moderator_module.py | 0 {src => kdb-bot/src}/modules/permission/__init__.py | 0 {src => kdb-bot/src}/modules/permission/abc/__init__.py | 0 .../src}/modules/permission/abc/permission_service_abc.py | 0 {src => kdb-bot/src}/modules/permission/configuration/__init__.py | 0 .../permission/configuration/permission_server_settings.py | 0 .../src}/modules/permission/configuration/permission_settings.py | 0 {src => kdb-bot/src}/modules/permission/events/__init__.py | 0 .../permission/events/permission_on_member_update_event.py | 0 .../src}/modules/permission/events/permission_on_ready_event.py | 0 {src => kdb-bot/src}/modules/permission/permission.json | 0 {src => kdb-bot/src}/modules/permission/permission_module.py | 0 {src => kdb-bot/src}/modules/permission/service/__init__.py | 0 .../src}/modules/permission/service/permission_service.py | 0 216 files changed, 0 insertions(+), 0 deletions(-) rename cpl-workspace.json => kdb-bot/cpl-workspace.json (100%) rename docker-compose.yml => kdb-bot/docker-compose.yml (100%) rename dockerfile => kdb-bot/dockerfile (100%) rename {src => kdb-bot/src}/bot/__init__.py (100%) rename {src => kdb-bot/src}/bot/application.py (100%) rename {src => kdb-bot/src}/bot/bot (100%) rename {src => kdb-bot/src}/bot/bot.json (100%) rename {src => kdb-bot/src}/bot/config/appsettings.PC-Nick.json (100%) rename {src => kdb-bot/src}/bot/config/appsettings.development.json (100%) rename {src => kdb-bot/src}/bot/config/appsettings.edrafts-lapi.json (100%) rename {src => kdb-bot/src}/bot/config/appsettings.edrafts-pc-ubuntu.json (100%) rename {src => kdb-bot/src}/bot/config/appsettings.example.json (100%) rename {src => kdb-bot/src}/bot/config/appsettings.json (100%) rename {src => kdb-bot/src}/bot/config/appsettings.production.json (100%) rename {src => kdb-bot/src}/bot/config/appsettings.staging.json (100%) rename {src => kdb-bot/src}/bot/config/feature-flags.json (100%) rename {src => kdb-bot/src}/bot/main.py (100%) rename {src => kdb-bot/src}/bot/module_list.py (100%) rename {src => kdb-bot/src}/bot/startup.py (100%) rename {src => kdb-bot/src}/bot/startup_discord_extension.py (100%) rename {src => kdb-bot/src}/bot/startup_migration_extension.py (100%) rename {src => kdb-bot/src}/bot/startup_module_extension.py (100%) rename {src => kdb-bot/src}/bot/startup_settings_extension.py (100%) rename {src => kdb-bot/src}/bot/translation/de.json (100%) rename {src => kdb-bot/src}/bot_api/__init__.py (100%) rename {src => kdb-bot/src}/bot_api/abc/__init__.py (100%) rename {src => kdb-bot/src}/bot_api/abc/auth_service_abc.py (100%) rename {src => kdb-bot/src}/bot_api/abc/auth_user_transformer_abc.py (100%) rename {src => kdb-bot/src}/bot_api/abc/dto_abc.py (100%) rename {src => kdb-bot/src}/bot_api/abc/select_criteria_abc.py (100%) rename {src => kdb-bot/src}/bot_api/api.py (100%) rename {src => kdb-bot/src}/bot_api/api_module.py (100%) rename {src => kdb-bot/src}/bot_api/api_thread.py (100%) rename {src => kdb-bot/src}/bot_api/bot-api.json (100%) rename {src => kdb-bot/src}/bot_api/config/apisettings.development.json (100%) rename {src => kdb-bot/src}/bot_api/config/apisettings.edrafts-lapi.json (100%) rename {src => kdb-bot/src}/bot_api/config/apisettings.edrafts-pc-ubuntu.json (100%) rename {src => kdb-bot/src}/bot_api/config/apisettings.json (100%) rename {src => kdb-bot/src}/bot_api/config/apisettings.production.json (100%) rename {src => kdb-bot/src}/bot_api/config/apisettings.staging.json (100%) rename {src => kdb-bot/src}/bot_api/config/appsettings.PC-Nick.json (100%) rename {src => kdb-bot/src}/bot_api/configuration/__init__.py (100%) rename {src => kdb-bot/src}/bot_api/configuration/api_settings.py (100%) rename {src => kdb-bot/src}/bot_api/configuration/authentication_settings.py (100%) rename {src => kdb-bot/src}/bot_api/configuration/frontend_settings.py (100%) rename {src => kdb-bot/src}/bot_api/configuration/version_settings.py (100%) rename {src => kdb-bot/src}/bot_api/controller/__init__.py (100%) rename {src => kdb-bot/src}/bot_api/controller/auth_controller.py (100%) rename {src => kdb-bot/src}/bot_api/controller/gui_controller.py (100%) rename {src => kdb-bot/src}/bot_api/exception/__init__.py (100%) rename {src => kdb-bot/src}/bot_api/exception/service_error_code_enum.py (100%) rename {src => kdb-bot/src}/bot_api/exception/service_exception.py (100%) rename {src => kdb-bot/src}/bot_api/filter/__init__.py (100%) rename {src => kdb-bot/src}/bot_api/filter/auth_user_select_criteria.py (100%) rename {src => kdb-bot/src}/bot_api/json_processor.py (100%) rename {src => kdb-bot/src}/bot_api/logging/__init__.py (100%) rename {src => kdb-bot/src}/bot_api/logging/api_logger.py (100%) rename {src => kdb-bot/src}/bot_api/model/__init__.py (100%) rename {src => kdb-bot/src}/bot_api/model/auth_user_dto.py (100%) rename {src => kdb-bot/src}/bot_api/model/auth_user_filtered_result_dto.py (100%) rename {src => kdb-bot/src}/bot_api/model/email_string_dto.py (100%) rename {src => kdb-bot/src}/bot_api/model/error_dto.py (100%) rename {src => kdb-bot/src}/bot_api/model/reset_password_dto.py (100%) rename {src => kdb-bot/src}/bot_api/model/settings_dto.py (100%) rename {src => kdb-bot/src}/bot_api/model/token_dto.py (100%) rename {src => kdb-bot/src}/bot_api/model/update_auth_user_dto.py (100%) rename {src => kdb-bot/src}/bot_api/model/version_dto.py (100%) rename {src => kdb-bot/src}/bot_api/route/__init__.py (100%) rename {src => kdb-bot/src}/bot_api/route/route.py (100%) rename {src => kdb-bot/src}/bot_api/service/__init__.py (100%) rename {src => kdb-bot/src}/bot_api/service/auth_service.py (100%) rename {src => kdb-bot/src}/bot_api/transformer/__init__.py (100%) rename {src => kdb-bot/src}/bot_api/transformer/auth_user_transformer.py (100%) rename {src => kdb-bot/src}/bot_core/__init__.py (100%) rename {src => kdb-bot/src}/bot_core/abc/__init__.py (100%) rename {src => kdb-bot/src}/bot_core/abc/client_utils_service_abc.py (100%) rename {src => kdb-bot/src}/bot_core/abc/custom_file_logger_abc.py (100%) rename {src => kdb-bot/src}/bot_core/abc/message_service_abc.py (100%) rename {src => kdb-bot/src}/bot_core/abc/module_abc.py (100%) rename {src => kdb-bot/src}/bot_core/bot-core.json (100%) rename {src => kdb-bot/src}/bot_core/configuration/__init__.py (100%) rename {src => kdb-bot/src}/bot_core/configuration/bot_logging_settings.py (100%) rename {src => kdb-bot/src}/bot_core/configuration/bot_settings.py (100%) rename {src => kdb-bot/src}/bot_core/configuration/feature_flags_enum.py (100%) rename {src => kdb-bot/src}/bot_core/configuration/feature_flags_settings.py (100%) rename {src => kdb-bot/src}/bot_core/configuration/file_logging_settings.py (100%) rename {src => kdb-bot/src}/bot_core/configuration/server_settings.py (100%) rename {src => kdb-bot/src}/bot_core/core_extension/__init__.py (100%) rename {src => kdb-bot/src}/bot_core/core_extension/core_extension_module.py (100%) rename {src => kdb-bot/src}/bot_core/core_extension/core_extension_on_ready_event.py (100%) rename {src => kdb-bot/src}/bot_core/core_module.py (100%) rename {src => kdb-bot/src}/bot_core/events/__init__.py (100%) rename {src => kdb-bot/src}/bot_core/events/core_on_ready_event.py (100%) rename {src => kdb-bot/src}/bot_core/helper/__init__.py (100%) rename {src => kdb-bot/src}/bot_core/helper/log_message_helper.py (100%) rename {src => kdb-bot/src}/bot_core/logging/__init__.py (100%) rename {src => kdb-bot/src}/bot_core/logging/command_logger.py (100%) rename {src => kdb-bot/src}/bot_core/logging/database_logger.py (100%) rename {src => kdb-bot/src}/bot_core/logging/message_logger.py (100%) rename {src => kdb-bot/src}/bot_core/pipes/__init__.py (100%) rename {src => kdb-bot/src}/bot_core/pipes/date_time_offset_pipe.py (100%) rename {src => kdb-bot/src}/bot_core/service/__init__.py (100%) rename {src => kdb-bot/src}/bot_core/service/client_utils_service.py (100%) rename {src => kdb-bot/src}/bot_core/service/message_service.py (100%) rename {src => kdb-bot/src}/bot_data/__init__.py (100%) rename {src => kdb-bot/src}/bot_data/abc/__init__.py (100%) rename {src => kdb-bot/src}/bot_data/abc/auth_user_repository_abc.py (100%) rename {src => kdb-bot/src}/bot_data/abc/auto_role_repository_abc.py (100%) rename {src => kdb-bot/src}/bot_data/abc/client_repository_abc.py (100%) rename {src => kdb-bot/src}/bot_data/abc/known_user_repository_abc.py (100%) rename {src => kdb-bot/src}/bot_data/abc/migration_abc.py (100%) rename {src => kdb-bot/src}/bot_data/abc/server_repository_abc.py (100%) rename {src => kdb-bot/src}/bot_data/abc/user_joined_server_repository_abc.py (100%) rename {src => kdb-bot/src}/bot_data/abc/user_joined_voice_channel_abc.py (100%) rename {src => kdb-bot/src}/bot_data/abc/user_repository_abc.py (100%) rename {src => kdb-bot/src}/bot_data/bot-data.json (100%) rename {src => kdb-bot/src}/bot_data/data_module.py (100%) rename {src => kdb-bot/src}/bot_data/db_context.py (100%) rename {src => kdb-bot/src}/bot_data/filtered_result.py (100%) rename {src => kdb-bot/src}/bot_data/migration/__init__.py (100%) rename {src => kdb-bot/src}/bot_data/migration/api_migration.py (100%) rename {src => kdb-bot/src}/bot_data/migration/auto_role_migration.py (100%) rename {src => kdb-bot/src}/bot_data/migration/initial_migration.py (100%) rename {src => kdb-bot/src}/bot_data/model/__init__.py (100%) rename {src => kdb-bot/src}/bot_data/model/auth_role_enum.py (100%) rename {src => kdb-bot/src}/bot_data/model/auth_user.py (100%) rename {src => kdb-bot/src}/bot_data/model/auto_role.py (100%) rename {src => kdb-bot/src}/bot_data/model/auto_role_rule.py (100%) rename {src => kdb-bot/src}/bot_data/model/client.py (100%) rename {src => kdb-bot/src}/bot_data/model/known_user.py (100%) rename {src => kdb-bot/src}/bot_data/model/migration_history.py (100%) rename {src => kdb-bot/src}/bot_data/model/server.py (100%) rename {src => kdb-bot/src}/bot_data/model/user.py (100%) rename {src => kdb-bot/src}/bot_data/model/user_joined_server.py (100%) rename {src => kdb-bot/src}/bot_data/model/user_joined_voice_channel.py (100%) rename {src => kdb-bot/src}/bot_data/service/__init__.py (100%) rename {src => kdb-bot/src}/bot_data/service/auth_user_repository_service.py (100%) rename {src => kdb-bot/src}/bot_data/service/auto_role_repository_service.py (100%) rename {src => kdb-bot/src}/bot_data/service/client_repository_service.py (100%) rename {src => kdb-bot/src}/bot_data/service/known_user_repository_service.py (100%) rename {src => kdb-bot/src}/bot_data/service/migration_service.py (100%) rename {src => kdb-bot/src}/bot_data/service/server_repository_service.py (100%) rename {src => kdb-bot/src}/bot_data/service/user_joined_server_repository_service.py (100%) rename {src => kdb-bot/src}/bot_data/service/user_joined_voice_channel_service.py (100%) rename {src => kdb-bot/src}/bot_data/service/user_repository_service.py (100%) rename {src => kdb-bot/src}/modules/__init__.py (100%) rename {src => kdb-bot/src}/modules/admin/__init__.py (100%) rename {src => kdb-bot/src}/modules/admin/admin.json (100%) rename {src => kdb-bot/src}/modules/admin/admin_module.py (100%) rename {src => kdb-bot/src}/modules/admin/command/__init__.py (100%) rename {src => kdb-bot/src}/modules/admin/command/restart_command.py (100%) rename {src => kdb-bot/src}/modules/admin/command/shutdown_command.py (100%) rename {src => kdb-bot/src}/modules/auto_role/__init__.py (100%) rename {src => kdb-bot/src}/modules/auto_role/auto-role.json (100%) rename {src => kdb-bot/src}/modules/auto_role/auto_role_module.py (100%) rename {src => kdb-bot/src}/modules/auto_role/command/__init__.py (100%) rename {src => kdb-bot/src}/modules/auto_role/command/auto_role_group.py (100%) rename {src => kdb-bot/src}/modules/auto_role/events/__init__.py (100%) rename {src => kdb-bot/src}/modules/auto_role/events/auto_role_on_raw_reaction_add.py (100%) rename {src => kdb-bot/src}/modules/auto_role/events/auto_role_on_raw_reaction_remove.py (100%) rename {src => kdb-bot/src}/modules/auto_role/helper/__init__.py (100%) rename {src => kdb-bot/src}/modules/auto_role/helper/reaction_handler.py (100%) rename {src => kdb-bot/src}/modules/base/__init__.py (100%) rename {src => kdb-bot/src}/modules/base/abc/__init__.py (100%) rename {src => kdb-bot/src}/modules/base/abc/base_helper_abc.py (100%) rename {src => kdb-bot/src}/modules/base/base.json (100%) rename {src => kdb-bot/src}/modules/base/base_module.py (100%) rename {src => kdb-bot/src}/modules/base/command/__init__.py (100%) rename {src => kdb-bot/src}/modules/base/command/afk_command.py (100%) rename {src => kdb-bot/src}/modules/base/command/help_command.py (100%) rename {src => kdb-bot/src}/modules/base/command/info_command.py (100%) rename {src => kdb-bot/src}/modules/base/command/ping_command.py (100%) rename {src => kdb-bot/src}/modules/base/configuration/__init__.py (100%) rename {src => kdb-bot/src}/modules/base/configuration/base_server_settings.py (100%) rename {src => kdb-bot/src}/modules/base/configuration/base_settings.py (100%) rename {src => kdb-bot/src}/modules/base/events/__init__.py (100%) rename {src => kdb-bot/src}/modules/base/events/base_on_command_error_event.py (100%) rename {src => kdb-bot/src}/modules/base/events/base_on_command_event.py (100%) rename {src => kdb-bot/src}/modules/base/events/base_on_member_join_event.py (100%) rename {src => kdb-bot/src}/modules/base/events/base_on_member_remove_event.py (100%) rename {src => kdb-bot/src}/modules/base/events/base_on_message_event.py (100%) rename {src => kdb-bot/src}/modules/base/events/base_on_voice_state_update_event.py (100%) rename {src => kdb-bot/src}/modules/base/service/__init__.py (100%) rename {src => kdb-bot/src}/modules/base/service/base_helper_service.py (100%) rename {src => kdb-bot/src}/modules/boot_log/__init__.py (100%) rename {src => kdb-bot/src}/modules/boot_log/boot-log.json (100%) rename {src => kdb-bot/src}/modules/boot_log/boot_log_extension.py (100%) rename {src => kdb-bot/src}/modules/boot_log/boot_log_module.py (100%) rename {src => kdb-bot/src}/modules/boot_log/boot_log_on_ready_event.py (100%) rename {src => kdb-bot/src}/modules/boot_log/configuration/__init__.py (100%) rename {src => kdb-bot/src}/modules/boot_log/configuration/boot_log_server_settings.py (100%) rename {src => kdb-bot/src}/modules/boot_log/configuration/boot_log_settings.py (100%) rename {src => kdb-bot/src}/modules/database/__init__.py (100%) rename {src => kdb-bot/src}/modules/database/database.json (100%) rename {src => kdb-bot/src}/modules/database/database_extension.py (100%) rename {src => kdb-bot/src}/modules/database/database_module.py (100%) rename {src => kdb-bot/src}/modules/database/database_on_ready_event.py (100%) rename {src => kdb-bot/src}/modules/moderator/__init__.py (100%) rename {src => kdb-bot/src}/modules/moderator/command/__init__.py (100%) rename {src => kdb-bot/src}/modules/moderator/command/purge_command.py (100%) rename {src => kdb-bot/src}/modules/moderator/command/user_group.py (100%) rename {src => kdb-bot/src}/modules/moderator/moderator.json (100%) rename {src => kdb-bot/src}/modules/moderator/moderator_module.py (100%) rename {src => kdb-bot/src}/modules/permission/__init__.py (100%) rename {src => kdb-bot/src}/modules/permission/abc/__init__.py (100%) rename {src => kdb-bot/src}/modules/permission/abc/permission_service_abc.py (100%) rename {src => kdb-bot/src}/modules/permission/configuration/__init__.py (100%) rename {src => kdb-bot/src}/modules/permission/configuration/permission_server_settings.py (100%) rename {src => kdb-bot/src}/modules/permission/configuration/permission_settings.py (100%) rename {src => kdb-bot/src}/modules/permission/events/__init__.py (100%) rename {src => kdb-bot/src}/modules/permission/events/permission_on_member_update_event.py (100%) rename {src => kdb-bot/src}/modules/permission/events/permission_on_ready_event.py (100%) rename {src => kdb-bot/src}/modules/permission/permission.json (100%) rename {src => kdb-bot/src}/modules/permission/permission_module.py (100%) rename {src => kdb-bot/src}/modules/permission/service/__init__.py (100%) rename {src => kdb-bot/src}/modules/permission/service/permission_service.py (100%) diff --git a/cpl-workspace.json b/kdb-bot/cpl-workspace.json similarity index 100% rename from cpl-workspace.json rename to kdb-bot/cpl-workspace.json diff --git a/docker-compose.yml b/kdb-bot/docker-compose.yml similarity index 100% rename from docker-compose.yml rename to kdb-bot/docker-compose.yml diff --git a/dockerfile b/kdb-bot/dockerfile similarity index 100% rename from dockerfile rename to kdb-bot/dockerfile diff --git a/src/bot/__init__.py b/kdb-bot/src/bot/__init__.py similarity index 100% rename from src/bot/__init__.py rename to kdb-bot/src/bot/__init__.py diff --git a/src/bot/application.py b/kdb-bot/src/bot/application.py similarity index 100% rename from src/bot/application.py rename to kdb-bot/src/bot/application.py diff --git a/src/bot/bot b/kdb-bot/src/bot/bot similarity index 100% rename from src/bot/bot rename to kdb-bot/src/bot/bot diff --git a/src/bot/bot.json b/kdb-bot/src/bot/bot.json similarity index 100% rename from src/bot/bot.json rename to kdb-bot/src/bot/bot.json diff --git a/src/bot/config/appsettings.PC-Nick.json b/kdb-bot/src/bot/config/appsettings.PC-Nick.json similarity index 100% rename from src/bot/config/appsettings.PC-Nick.json rename to kdb-bot/src/bot/config/appsettings.PC-Nick.json diff --git a/src/bot/config/appsettings.development.json b/kdb-bot/src/bot/config/appsettings.development.json similarity index 100% rename from src/bot/config/appsettings.development.json rename to kdb-bot/src/bot/config/appsettings.development.json diff --git a/src/bot/config/appsettings.edrafts-lapi.json b/kdb-bot/src/bot/config/appsettings.edrafts-lapi.json similarity index 100% rename from src/bot/config/appsettings.edrafts-lapi.json rename to kdb-bot/src/bot/config/appsettings.edrafts-lapi.json diff --git a/src/bot/config/appsettings.edrafts-pc-ubuntu.json b/kdb-bot/src/bot/config/appsettings.edrafts-pc-ubuntu.json similarity index 100% rename from src/bot/config/appsettings.edrafts-pc-ubuntu.json rename to kdb-bot/src/bot/config/appsettings.edrafts-pc-ubuntu.json diff --git a/src/bot/config/appsettings.example.json b/kdb-bot/src/bot/config/appsettings.example.json similarity index 100% rename from src/bot/config/appsettings.example.json rename to kdb-bot/src/bot/config/appsettings.example.json diff --git a/src/bot/config/appsettings.json b/kdb-bot/src/bot/config/appsettings.json similarity index 100% rename from src/bot/config/appsettings.json rename to kdb-bot/src/bot/config/appsettings.json diff --git a/src/bot/config/appsettings.production.json b/kdb-bot/src/bot/config/appsettings.production.json similarity index 100% rename from src/bot/config/appsettings.production.json rename to kdb-bot/src/bot/config/appsettings.production.json diff --git a/src/bot/config/appsettings.staging.json b/kdb-bot/src/bot/config/appsettings.staging.json similarity index 100% rename from src/bot/config/appsettings.staging.json rename to kdb-bot/src/bot/config/appsettings.staging.json diff --git a/src/bot/config/feature-flags.json b/kdb-bot/src/bot/config/feature-flags.json similarity index 100% rename from src/bot/config/feature-flags.json rename to kdb-bot/src/bot/config/feature-flags.json diff --git a/src/bot/main.py b/kdb-bot/src/bot/main.py similarity index 100% rename from src/bot/main.py rename to kdb-bot/src/bot/main.py diff --git a/src/bot/module_list.py b/kdb-bot/src/bot/module_list.py similarity index 100% rename from src/bot/module_list.py rename to kdb-bot/src/bot/module_list.py diff --git a/src/bot/startup.py b/kdb-bot/src/bot/startup.py similarity index 100% rename from src/bot/startup.py rename to kdb-bot/src/bot/startup.py diff --git a/src/bot/startup_discord_extension.py b/kdb-bot/src/bot/startup_discord_extension.py similarity index 100% rename from src/bot/startup_discord_extension.py rename to kdb-bot/src/bot/startup_discord_extension.py diff --git a/src/bot/startup_migration_extension.py b/kdb-bot/src/bot/startup_migration_extension.py similarity index 100% rename from src/bot/startup_migration_extension.py rename to kdb-bot/src/bot/startup_migration_extension.py diff --git a/src/bot/startup_module_extension.py b/kdb-bot/src/bot/startup_module_extension.py similarity index 100% rename from src/bot/startup_module_extension.py rename to kdb-bot/src/bot/startup_module_extension.py diff --git a/src/bot/startup_settings_extension.py b/kdb-bot/src/bot/startup_settings_extension.py similarity index 100% rename from src/bot/startup_settings_extension.py rename to kdb-bot/src/bot/startup_settings_extension.py diff --git a/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json similarity index 100% rename from src/bot/translation/de.json rename to kdb-bot/src/bot/translation/de.json diff --git a/src/bot_api/__init__.py b/kdb-bot/src/bot_api/__init__.py similarity index 100% rename from src/bot_api/__init__.py rename to kdb-bot/src/bot_api/__init__.py diff --git a/src/bot_api/abc/__init__.py b/kdb-bot/src/bot_api/abc/__init__.py similarity index 100% rename from src/bot_api/abc/__init__.py rename to kdb-bot/src/bot_api/abc/__init__.py diff --git a/src/bot_api/abc/auth_service_abc.py b/kdb-bot/src/bot_api/abc/auth_service_abc.py similarity index 100% rename from src/bot_api/abc/auth_service_abc.py rename to kdb-bot/src/bot_api/abc/auth_service_abc.py diff --git a/src/bot_api/abc/auth_user_transformer_abc.py b/kdb-bot/src/bot_api/abc/auth_user_transformer_abc.py similarity index 100% rename from src/bot_api/abc/auth_user_transformer_abc.py rename to kdb-bot/src/bot_api/abc/auth_user_transformer_abc.py diff --git a/src/bot_api/abc/dto_abc.py b/kdb-bot/src/bot_api/abc/dto_abc.py similarity index 100% rename from src/bot_api/abc/dto_abc.py rename to kdb-bot/src/bot_api/abc/dto_abc.py diff --git a/src/bot_api/abc/select_criteria_abc.py b/kdb-bot/src/bot_api/abc/select_criteria_abc.py similarity index 100% rename from src/bot_api/abc/select_criteria_abc.py rename to kdb-bot/src/bot_api/abc/select_criteria_abc.py diff --git a/src/bot_api/api.py b/kdb-bot/src/bot_api/api.py similarity index 100% rename from src/bot_api/api.py rename to kdb-bot/src/bot_api/api.py diff --git a/src/bot_api/api_module.py b/kdb-bot/src/bot_api/api_module.py similarity index 100% rename from src/bot_api/api_module.py rename to kdb-bot/src/bot_api/api_module.py diff --git a/src/bot_api/api_thread.py b/kdb-bot/src/bot_api/api_thread.py similarity index 100% rename from src/bot_api/api_thread.py rename to kdb-bot/src/bot_api/api_thread.py diff --git a/src/bot_api/bot-api.json b/kdb-bot/src/bot_api/bot-api.json similarity index 100% rename from src/bot_api/bot-api.json rename to kdb-bot/src/bot_api/bot-api.json diff --git a/src/bot_api/config/apisettings.development.json b/kdb-bot/src/bot_api/config/apisettings.development.json similarity index 100% rename from src/bot_api/config/apisettings.development.json rename to kdb-bot/src/bot_api/config/apisettings.development.json diff --git a/src/bot_api/config/apisettings.edrafts-lapi.json b/kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json similarity index 100% rename from src/bot_api/config/apisettings.edrafts-lapi.json rename to kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json diff --git a/src/bot_api/config/apisettings.edrafts-pc-ubuntu.json b/kdb-bot/src/bot_api/config/apisettings.edrafts-pc-ubuntu.json similarity index 100% rename from src/bot_api/config/apisettings.edrafts-pc-ubuntu.json rename to kdb-bot/src/bot_api/config/apisettings.edrafts-pc-ubuntu.json diff --git a/src/bot_api/config/apisettings.json b/kdb-bot/src/bot_api/config/apisettings.json similarity index 100% rename from src/bot_api/config/apisettings.json rename to kdb-bot/src/bot_api/config/apisettings.json diff --git a/src/bot_api/config/apisettings.production.json b/kdb-bot/src/bot_api/config/apisettings.production.json similarity index 100% rename from src/bot_api/config/apisettings.production.json rename to kdb-bot/src/bot_api/config/apisettings.production.json diff --git a/src/bot_api/config/apisettings.staging.json b/kdb-bot/src/bot_api/config/apisettings.staging.json similarity index 100% rename from src/bot_api/config/apisettings.staging.json rename to kdb-bot/src/bot_api/config/apisettings.staging.json diff --git a/src/bot_api/config/appsettings.PC-Nick.json b/kdb-bot/src/bot_api/config/appsettings.PC-Nick.json similarity index 100% rename from src/bot_api/config/appsettings.PC-Nick.json rename to kdb-bot/src/bot_api/config/appsettings.PC-Nick.json diff --git a/src/bot_api/configuration/__init__.py b/kdb-bot/src/bot_api/configuration/__init__.py similarity index 100% rename from src/bot_api/configuration/__init__.py rename to kdb-bot/src/bot_api/configuration/__init__.py diff --git a/src/bot_api/configuration/api_settings.py b/kdb-bot/src/bot_api/configuration/api_settings.py similarity index 100% rename from src/bot_api/configuration/api_settings.py rename to kdb-bot/src/bot_api/configuration/api_settings.py diff --git a/src/bot_api/configuration/authentication_settings.py b/kdb-bot/src/bot_api/configuration/authentication_settings.py similarity index 100% rename from src/bot_api/configuration/authentication_settings.py rename to kdb-bot/src/bot_api/configuration/authentication_settings.py diff --git a/src/bot_api/configuration/frontend_settings.py b/kdb-bot/src/bot_api/configuration/frontend_settings.py similarity index 100% rename from src/bot_api/configuration/frontend_settings.py rename to kdb-bot/src/bot_api/configuration/frontend_settings.py diff --git a/src/bot_api/configuration/version_settings.py b/kdb-bot/src/bot_api/configuration/version_settings.py similarity index 100% rename from src/bot_api/configuration/version_settings.py rename to kdb-bot/src/bot_api/configuration/version_settings.py diff --git a/src/bot_api/controller/__init__.py b/kdb-bot/src/bot_api/controller/__init__.py similarity index 100% rename from src/bot_api/controller/__init__.py rename to kdb-bot/src/bot_api/controller/__init__.py diff --git a/src/bot_api/controller/auth_controller.py b/kdb-bot/src/bot_api/controller/auth_controller.py similarity index 100% rename from src/bot_api/controller/auth_controller.py rename to kdb-bot/src/bot_api/controller/auth_controller.py diff --git a/src/bot_api/controller/gui_controller.py b/kdb-bot/src/bot_api/controller/gui_controller.py similarity index 100% rename from src/bot_api/controller/gui_controller.py rename to kdb-bot/src/bot_api/controller/gui_controller.py diff --git a/src/bot_api/exception/__init__.py b/kdb-bot/src/bot_api/exception/__init__.py similarity index 100% rename from src/bot_api/exception/__init__.py rename to kdb-bot/src/bot_api/exception/__init__.py diff --git a/src/bot_api/exception/service_error_code_enum.py b/kdb-bot/src/bot_api/exception/service_error_code_enum.py similarity index 100% rename from src/bot_api/exception/service_error_code_enum.py rename to kdb-bot/src/bot_api/exception/service_error_code_enum.py diff --git a/src/bot_api/exception/service_exception.py b/kdb-bot/src/bot_api/exception/service_exception.py similarity index 100% rename from src/bot_api/exception/service_exception.py rename to kdb-bot/src/bot_api/exception/service_exception.py diff --git a/src/bot_api/filter/__init__.py b/kdb-bot/src/bot_api/filter/__init__.py similarity index 100% rename from src/bot_api/filter/__init__.py rename to kdb-bot/src/bot_api/filter/__init__.py diff --git a/src/bot_api/filter/auth_user_select_criteria.py b/kdb-bot/src/bot_api/filter/auth_user_select_criteria.py similarity index 100% rename from src/bot_api/filter/auth_user_select_criteria.py rename to kdb-bot/src/bot_api/filter/auth_user_select_criteria.py diff --git a/src/bot_api/json_processor.py b/kdb-bot/src/bot_api/json_processor.py similarity index 100% rename from src/bot_api/json_processor.py rename to kdb-bot/src/bot_api/json_processor.py diff --git a/src/bot_api/logging/__init__.py b/kdb-bot/src/bot_api/logging/__init__.py similarity index 100% rename from src/bot_api/logging/__init__.py rename to kdb-bot/src/bot_api/logging/__init__.py diff --git a/src/bot_api/logging/api_logger.py b/kdb-bot/src/bot_api/logging/api_logger.py similarity index 100% rename from src/bot_api/logging/api_logger.py rename to kdb-bot/src/bot_api/logging/api_logger.py diff --git a/src/bot_api/model/__init__.py b/kdb-bot/src/bot_api/model/__init__.py similarity index 100% rename from src/bot_api/model/__init__.py rename to kdb-bot/src/bot_api/model/__init__.py diff --git a/src/bot_api/model/auth_user_dto.py b/kdb-bot/src/bot_api/model/auth_user_dto.py similarity index 100% rename from src/bot_api/model/auth_user_dto.py rename to kdb-bot/src/bot_api/model/auth_user_dto.py diff --git a/src/bot_api/model/auth_user_filtered_result_dto.py b/kdb-bot/src/bot_api/model/auth_user_filtered_result_dto.py similarity index 100% rename from src/bot_api/model/auth_user_filtered_result_dto.py rename to kdb-bot/src/bot_api/model/auth_user_filtered_result_dto.py diff --git a/src/bot_api/model/email_string_dto.py b/kdb-bot/src/bot_api/model/email_string_dto.py similarity index 100% rename from src/bot_api/model/email_string_dto.py rename to kdb-bot/src/bot_api/model/email_string_dto.py diff --git a/src/bot_api/model/error_dto.py b/kdb-bot/src/bot_api/model/error_dto.py similarity index 100% rename from src/bot_api/model/error_dto.py rename to kdb-bot/src/bot_api/model/error_dto.py diff --git a/src/bot_api/model/reset_password_dto.py b/kdb-bot/src/bot_api/model/reset_password_dto.py similarity index 100% rename from src/bot_api/model/reset_password_dto.py rename to kdb-bot/src/bot_api/model/reset_password_dto.py diff --git a/src/bot_api/model/settings_dto.py b/kdb-bot/src/bot_api/model/settings_dto.py similarity index 100% rename from src/bot_api/model/settings_dto.py rename to kdb-bot/src/bot_api/model/settings_dto.py diff --git a/src/bot_api/model/token_dto.py b/kdb-bot/src/bot_api/model/token_dto.py similarity index 100% rename from src/bot_api/model/token_dto.py rename to kdb-bot/src/bot_api/model/token_dto.py diff --git a/src/bot_api/model/update_auth_user_dto.py b/kdb-bot/src/bot_api/model/update_auth_user_dto.py similarity index 100% rename from src/bot_api/model/update_auth_user_dto.py rename to kdb-bot/src/bot_api/model/update_auth_user_dto.py diff --git a/src/bot_api/model/version_dto.py b/kdb-bot/src/bot_api/model/version_dto.py similarity index 100% rename from src/bot_api/model/version_dto.py rename to kdb-bot/src/bot_api/model/version_dto.py diff --git a/src/bot_api/route/__init__.py b/kdb-bot/src/bot_api/route/__init__.py similarity index 100% rename from src/bot_api/route/__init__.py rename to kdb-bot/src/bot_api/route/__init__.py diff --git a/src/bot_api/route/route.py b/kdb-bot/src/bot_api/route/route.py similarity index 100% rename from src/bot_api/route/route.py rename to kdb-bot/src/bot_api/route/route.py diff --git a/src/bot_api/service/__init__.py b/kdb-bot/src/bot_api/service/__init__.py similarity index 100% rename from src/bot_api/service/__init__.py rename to kdb-bot/src/bot_api/service/__init__.py diff --git a/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py similarity index 100% rename from src/bot_api/service/auth_service.py rename to kdb-bot/src/bot_api/service/auth_service.py diff --git a/src/bot_api/transformer/__init__.py b/kdb-bot/src/bot_api/transformer/__init__.py similarity index 100% rename from src/bot_api/transformer/__init__.py rename to kdb-bot/src/bot_api/transformer/__init__.py diff --git a/src/bot_api/transformer/auth_user_transformer.py b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py similarity index 100% rename from src/bot_api/transformer/auth_user_transformer.py rename to kdb-bot/src/bot_api/transformer/auth_user_transformer.py diff --git a/src/bot_core/__init__.py b/kdb-bot/src/bot_core/__init__.py similarity index 100% rename from src/bot_core/__init__.py rename to kdb-bot/src/bot_core/__init__.py diff --git a/src/bot_core/abc/__init__.py b/kdb-bot/src/bot_core/abc/__init__.py similarity index 100% rename from src/bot_core/abc/__init__.py rename to kdb-bot/src/bot_core/abc/__init__.py diff --git a/src/bot_core/abc/client_utils_service_abc.py b/kdb-bot/src/bot_core/abc/client_utils_service_abc.py similarity index 100% rename from src/bot_core/abc/client_utils_service_abc.py rename to kdb-bot/src/bot_core/abc/client_utils_service_abc.py diff --git a/src/bot_core/abc/custom_file_logger_abc.py b/kdb-bot/src/bot_core/abc/custom_file_logger_abc.py similarity index 100% rename from src/bot_core/abc/custom_file_logger_abc.py rename to kdb-bot/src/bot_core/abc/custom_file_logger_abc.py diff --git a/src/bot_core/abc/message_service_abc.py b/kdb-bot/src/bot_core/abc/message_service_abc.py similarity index 100% rename from src/bot_core/abc/message_service_abc.py rename to kdb-bot/src/bot_core/abc/message_service_abc.py diff --git a/src/bot_core/abc/module_abc.py b/kdb-bot/src/bot_core/abc/module_abc.py similarity index 100% rename from src/bot_core/abc/module_abc.py rename to kdb-bot/src/bot_core/abc/module_abc.py diff --git a/src/bot_core/bot-core.json b/kdb-bot/src/bot_core/bot-core.json similarity index 100% rename from src/bot_core/bot-core.json rename to kdb-bot/src/bot_core/bot-core.json diff --git a/src/bot_core/configuration/__init__.py b/kdb-bot/src/bot_core/configuration/__init__.py similarity index 100% rename from src/bot_core/configuration/__init__.py rename to kdb-bot/src/bot_core/configuration/__init__.py diff --git a/src/bot_core/configuration/bot_logging_settings.py b/kdb-bot/src/bot_core/configuration/bot_logging_settings.py similarity index 100% rename from src/bot_core/configuration/bot_logging_settings.py rename to kdb-bot/src/bot_core/configuration/bot_logging_settings.py diff --git a/src/bot_core/configuration/bot_settings.py b/kdb-bot/src/bot_core/configuration/bot_settings.py similarity index 100% rename from src/bot_core/configuration/bot_settings.py rename to kdb-bot/src/bot_core/configuration/bot_settings.py diff --git a/src/bot_core/configuration/feature_flags_enum.py b/kdb-bot/src/bot_core/configuration/feature_flags_enum.py similarity index 100% rename from src/bot_core/configuration/feature_flags_enum.py rename to kdb-bot/src/bot_core/configuration/feature_flags_enum.py diff --git a/src/bot_core/configuration/feature_flags_settings.py b/kdb-bot/src/bot_core/configuration/feature_flags_settings.py similarity index 100% rename from src/bot_core/configuration/feature_flags_settings.py rename to kdb-bot/src/bot_core/configuration/feature_flags_settings.py diff --git a/src/bot_core/configuration/file_logging_settings.py b/kdb-bot/src/bot_core/configuration/file_logging_settings.py similarity index 100% rename from src/bot_core/configuration/file_logging_settings.py rename to kdb-bot/src/bot_core/configuration/file_logging_settings.py diff --git a/src/bot_core/configuration/server_settings.py b/kdb-bot/src/bot_core/configuration/server_settings.py similarity index 100% rename from src/bot_core/configuration/server_settings.py rename to kdb-bot/src/bot_core/configuration/server_settings.py diff --git a/src/bot_core/core_extension/__init__.py b/kdb-bot/src/bot_core/core_extension/__init__.py similarity index 100% rename from src/bot_core/core_extension/__init__.py rename to kdb-bot/src/bot_core/core_extension/__init__.py diff --git a/src/bot_core/core_extension/core_extension_module.py b/kdb-bot/src/bot_core/core_extension/core_extension_module.py similarity index 100% rename from src/bot_core/core_extension/core_extension_module.py rename to kdb-bot/src/bot_core/core_extension/core_extension_module.py diff --git a/src/bot_core/core_extension/core_extension_on_ready_event.py b/kdb-bot/src/bot_core/core_extension/core_extension_on_ready_event.py similarity index 100% rename from src/bot_core/core_extension/core_extension_on_ready_event.py rename to kdb-bot/src/bot_core/core_extension/core_extension_on_ready_event.py diff --git a/src/bot_core/core_module.py b/kdb-bot/src/bot_core/core_module.py similarity index 100% rename from src/bot_core/core_module.py rename to kdb-bot/src/bot_core/core_module.py diff --git a/src/bot_core/events/__init__.py b/kdb-bot/src/bot_core/events/__init__.py similarity index 100% rename from src/bot_core/events/__init__.py rename to kdb-bot/src/bot_core/events/__init__.py diff --git a/src/bot_core/events/core_on_ready_event.py b/kdb-bot/src/bot_core/events/core_on_ready_event.py similarity index 100% rename from src/bot_core/events/core_on_ready_event.py rename to kdb-bot/src/bot_core/events/core_on_ready_event.py diff --git a/src/bot_core/helper/__init__.py b/kdb-bot/src/bot_core/helper/__init__.py similarity index 100% rename from src/bot_core/helper/__init__.py rename to kdb-bot/src/bot_core/helper/__init__.py diff --git a/src/bot_core/helper/log_message_helper.py b/kdb-bot/src/bot_core/helper/log_message_helper.py similarity index 100% rename from src/bot_core/helper/log_message_helper.py rename to kdb-bot/src/bot_core/helper/log_message_helper.py diff --git a/src/bot_core/logging/__init__.py b/kdb-bot/src/bot_core/logging/__init__.py similarity index 100% rename from src/bot_core/logging/__init__.py rename to kdb-bot/src/bot_core/logging/__init__.py diff --git a/src/bot_core/logging/command_logger.py b/kdb-bot/src/bot_core/logging/command_logger.py similarity index 100% rename from src/bot_core/logging/command_logger.py rename to kdb-bot/src/bot_core/logging/command_logger.py diff --git a/src/bot_core/logging/database_logger.py b/kdb-bot/src/bot_core/logging/database_logger.py similarity index 100% rename from src/bot_core/logging/database_logger.py rename to kdb-bot/src/bot_core/logging/database_logger.py diff --git a/src/bot_core/logging/message_logger.py b/kdb-bot/src/bot_core/logging/message_logger.py similarity index 100% rename from src/bot_core/logging/message_logger.py rename to kdb-bot/src/bot_core/logging/message_logger.py diff --git a/src/bot_core/pipes/__init__.py b/kdb-bot/src/bot_core/pipes/__init__.py similarity index 100% rename from src/bot_core/pipes/__init__.py rename to kdb-bot/src/bot_core/pipes/__init__.py diff --git a/src/bot_core/pipes/date_time_offset_pipe.py b/kdb-bot/src/bot_core/pipes/date_time_offset_pipe.py similarity index 100% rename from src/bot_core/pipes/date_time_offset_pipe.py rename to kdb-bot/src/bot_core/pipes/date_time_offset_pipe.py diff --git a/src/bot_core/service/__init__.py b/kdb-bot/src/bot_core/service/__init__.py similarity index 100% rename from src/bot_core/service/__init__.py rename to kdb-bot/src/bot_core/service/__init__.py diff --git a/src/bot_core/service/client_utils_service.py b/kdb-bot/src/bot_core/service/client_utils_service.py similarity index 100% rename from src/bot_core/service/client_utils_service.py rename to kdb-bot/src/bot_core/service/client_utils_service.py diff --git a/src/bot_core/service/message_service.py b/kdb-bot/src/bot_core/service/message_service.py similarity index 100% rename from src/bot_core/service/message_service.py rename to kdb-bot/src/bot_core/service/message_service.py diff --git a/src/bot_data/__init__.py b/kdb-bot/src/bot_data/__init__.py similarity index 100% rename from src/bot_data/__init__.py rename to kdb-bot/src/bot_data/__init__.py diff --git a/src/bot_data/abc/__init__.py b/kdb-bot/src/bot_data/abc/__init__.py similarity index 100% rename from src/bot_data/abc/__init__.py rename to kdb-bot/src/bot_data/abc/__init__.py diff --git a/src/bot_data/abc/auth_user_repository_abc.py b/kdb-bot/src/bot_data/abc/auth_user_repository_abc.py similarity index 100% rename from src/bot_data/abc/auth_user_repository_abc.py rename to kdb-bot/src/bot_data/abc/auth_user_repository_abc.py diff --git a/src/bot_data/abc/auto_role_repository_abc.py b/kdb-bot/src/bot_data/abc/auto_role_repository_abc.py similarity index 100% rename from src/bot_data/abc/auto_role_repository_abc.py rename to kdb-bot/src/bot_data/abc/auto_role_repository_abc.py diff --git a/src/bot_data/abc/client_repository_abc.py b/kdb-bot/src/bot_data/abc/client_repository_abc.py similarity index 100% rename from src/bot_data/abc/client_repository_abc.py rename to kdb-bot/src/bot_data/abc/client_repository_abc.py diff --git a/src/bot_data/abc/known_user_repository_abc.py b/kdb-bot/src/bot_data/abc/known_user_repository_abc.py similarity index 100% rename from src/bot_data/abc/known_user_repository_abc.py rename to kdb-bot/src/bot_data/abc/known_user_repository_abc.py diff --git a/src/bot_data/abc/migration_abc.py b/kdb-bot/src/bot_data/abc/migration_abc.py similarity index 100% rename from src/bot_data/abc/migration_abc.py rename to kdb-bot/src/bot_data/abc/migration_abc.py diff --git a/src/bot_data/abc/server_repository_abc.py b/kdb-bot/src/bot_data/abc/server_repository_abc.py similarity index 100% rename from src/bot_data/abc/server_repository_abc.py rename to kdb-bot/src/bot_data/abc/server_repository_abc.py diff --git a/src/bot_data/abc/user_joined_server_repository_abc.py b/kdb-bot/src/bot_data/abc/user_joined_server_repository_abc.py similarity index 100% rename from src/bot_data/abc/user_joined_server_repository_abc.py rename to kdb-bot/src/bot_data/abc/user_joined_server_repository_abc.py diff --git a/src/bot_data/abc/user_joined_voice_channel_abc.py b/kdb-bot/src/bot_data/abc/user_joined_voice_channel_abc.py similarity index 100% rename from src/bot_data/abc/user_joined_voice_channel_abc.py rename to kdb-bot/src/bot_data/abc/user_joined_voice_channel_abc.py diff --git a/src/bot_data/abc/user_repository_abc.py b/kdb-bot/src/bot_data/abc/user_repository_abc.py similarity index 100% rename from src/bot_data/abc/user_repository_abc.py rename to kdb-bot/src/bot_data/abc/user_repository_abc.py diff --git a/src/bot_data/bot-data.json b/kdb-bot/src/bot_data/bot-data.json similarity index 100% rename from src/bot_data/bot-data.json rename to kdb-bot/src/bot_data/bot-data.json diff --git a/src/bot_data/data_module.py b/kdb-bot/src/bot_data/data_module.py similarity index 100% rename from src/bot_data/data_module.py rename to kdb-bot/src/bot_data/data_module.py diff --git a/src/bot_data/db_context.py b/kdb-bot/src/bot_data/db_context.py similarity index 100% rename from src/bot_data/db_context.py rename to kdb-bot/src/bot_data/db_context.py diff --git a/src/bot_data/filtered_result.py b/kdb-bot/src/bot_data/filtered_result.py similarity index 100% rename from src/bot_data/filtered_result.py rename to kdb-bot/src/bot_data/filtered_result.py diff --git a/src/bot_data/migration/__init__.py b/kdb-bot/src/bot_data/migration/__init__.py similarity index 100% rename from src/bot_data/migration/__init__.py rename to kdb-bot/src/bot_data/migration/__init__.py diff --git a/src/bot_data/migration/api_migration.py b/kdb-bot/src/bot_data/migration/api_migration.py similarity index 100% rename from src/bot_data/migration/api_migration.py rename to kdb-bot/src/bot_data/migration/api_migration.py diff --git a/src/bot_data/migration/auto_role_migration.py b/kdb-bot/src/bot_data/migration/auto_role_migration.py similarity index 100% rename from src/bot_data/migration/auto_role_migration.py rename to kdb-bot/src/bot_data/migration/auto_role_migration.py diff --git a/src/bot_data/migration/initial_migration.py b/kdb-bot/src/bot_data/migration/initial_migration.py similarity index 100% rename from src/bot_data/migration/initial_migration.py rename to kdb-bot/src/bot_data/migration/initial_migration.py diff --git a/src/bot_data/model/__init__.py b/kdb-bot/src/bot_data/model/__init__.py similarity index 100% rename from src/bot_data/model/__init__.py rename to kdb-bot/src/bot_data/model/__init__.py diff --git a/src/bot_data/model/auth_role_enum.py b/kdb-bot/src/bot_data/model/auth_role_enum.py similarity index 100% rename from src/bot_data/model/auth_role_enum.py rename to kdb-bot/src/bot_data/model/auth_role_enum.py diff --git a/src/bot_data/model/auth_user.py b/kdb-bot/src/bot_data/model/auth_user.py similarity index 100% rename from src/bot_data/model/auth_user.py rename to kdb-bot/src/bot_data/model/auth_user.py diff --git a/src/bot_data/model/auto_role.py b/kdb-bot/src/bot_data/model/auto_role.py similarity index 100% rename from src/bot_data/model/auto_role.py rename to kdb-bot/src/bot_data/model/auto_role.py diff --git a/src/bot_data/model/auto_role_rule.py b/kdb-bot/src/bot_data/model/auto_role_rule.py similarity index 100% rename from src/bot_data/model/auto_role_rule.py rename to kdb-bot/src/bot_data/model/auto_role_rule.py diff --git a/src/bot_data/model/client.py b/kdb-bot/src/bot_data/model/client.py similarity index 100% rename from src/bot_data/model/client.py rename to kdb-bot/src/bot_data/model/client.py diff --git a/src/bot_data/model/known_user.py b/kdb-bot/src/bot_data/model/known_user.py similarity index 100% rename from src/bot_data/model/known_user.py rename to kdb-bot/src/bot_data/model/known_user.py diff --git a/src/bot_data/model/migration_history.py b/kdb-bot/src/bot_data/model/migration_history.py similarity index 100% rename from src/bot_data/model/migration_history.py rename to kdb-bot/src/bot_data/model/migration_history.py diff --git a/src/bot_data/model/server.py b/kdb-bot/src/bot_data/model/server.py similarity index 100% rename from src/bot_data/model/server.py rename to kdb-bot/src/bot_data/model/server.py diff --git a/src/bot_data/model/user.py b/kdb-bot/src/bot_data/model/user.py similarity index 100% rename from src/bot_data/model/user.py rename to kdb-bot/src/bot_data/model/user.py diff --git a/src/bot_data/model/user_joined_server.py b/kdb-bot/src/bot_data/model/user_joined_server.py similarity index 100% rename from src/bot_data/model/user_joined_server.py rename to kdb-bot/src/bot_data/model/user_joined_server.py diff --git a/src/bot_data/model/user_joined_voice_channel.py b/kdb-bot/src/bot_data/model/user_joined_voice_channel.py similarity index 100% rename from src/bot_data/model/user_joined_voice_channel.py rename to kdb-bot/src/bot_data/model/user_joined_voice_channel.py diff --git a/src/bot_data/service/__init__.py b/kdb-bot/src/bot_data/service/__init__.py similarity index 100% rename from src/bot_data/service/__init__.py rename to kdb-bot/src/bot_data/service/__init__.py diff --git a/src/bot_data/service/auth_user_repository_service.py b/kdb-bot/src/bot_data/service/auth_user_repository_service.py similarity index 100% rename from src/bot_data/service/auth_user_repository_service.py rename to kdb-bot/src/bot_data/service/auth_user_repository_service.py diff --git a/src/bot_data/service/auto_role_repository_service.py b/kdb-bot/src/bot_data/service/auto_role_repository_service.py similarity index 100% rename from src/bot_data/service/auto_role_repository_service.py rename to kdb-bot/src/bot_data/service/auto_role_repository_service.py diff --git a/src/bot_data/service/client_repository_service.py b/kdb-bot/src/bot_data/service/client_repository_service.py similarity index 100% rename from src/bot_data/service/client_repository_service.py rename to kdb-bot/src/bot_data/service/client_repository_service.py diff --git a/src/bot_data/service/known_user_repository_service.py b/kdb-bot/src/bot_data/service/known_user_repository_service.py similarity index 100% rename from src/bot_data/service/known_user_repository_service.py rename to kdb-bot/src/bot_data/service/known_user_repository_service.py diff --git a/src/bot_data/service/migration_service.py b/kdb-bot/src/bot_data/service/migration_service.py similarity index 100% rename from src/bot_data/service/migration_service.py rename to kdb-bot/src/bot_data/service/migration_service.py diff --git a/src/bot_data/service/server_repository_service.py b/kdb-bot/src/bot_data/service/server_repository_service.py similarity index 100% rename from src/bot_data/service/server_repository_service.py rename to kdb-bot/src/bot_data/service/server_repository_service.py diff --git a/src/bot_data/service/user_joined_server_repository_service.py b/kdb-bot/src/bot_data/service/user_joined_server_repository_service.py similarity index 100% rename from src/bot_data/service/user_joined_server_repository_service.py rename to kdb-bot/src/bot_data/service/user_joined_server_repository_service.py diff --git a/src/bot_data/service/user_joined_voice_channel_service.py b/kdb-bot/src/bot_data/service/user_joined_voice_channel_service.py similarity index 100% rename from src/bot_data/service/user_joined_voice_channel_service.py rename to kdb-bot/src/bot_data/service/user_joined_voice_channel_service.py diff --git a/src/bot_data/service/user_repository_service.py b/kdb-bot/src/bot_data/service/user_repository_service.py similarity index 100% rename from src/bot_data/service/user_repository_service.py rename to kdb-bot/src/bot_data/service/user_repository_service.py diff --git a/src/modules/__init__.py b/kdb-bot/src/modules/__init__.py similarity index 100% rename from src/modules/__init__.py rename to kdb-bot/src/modules/__init__.py diff --git a/src/modules/admin/__init__.py b/kdb-bot/src/modules/admin/__init__.py similarity index 100% rename from src/modules/admin/__init__.py rename to kdb-bot/src/modules/admin/__init__.py diff --git a/src/modules/admin/admin.json b/kdb-bot/src/modules/admin/admin.json similarity index 100% rename from src/modules/admin/admin.json rename to kdb-bot/src/modules/admin/admin.json diff --git a/src/modules/admin/admin_module.py b/kdb-bot/src/modules/admin/admin_module.py similarity index 100% rename from src/modules/admin/admin_module.py rename to kdb-bot/src/modules/admin/admin_module.py diff --git a/src/modules/admin/command/__init__.py b/kdb-bot/src/modules/admin/command/__init__.py similarity index 100% rename from src/modules/admin/command/__init__.py rename to kdb-bot/src/modules/admin/command/__init__.py diff --git a/src/modules/admin/command/restart_command.py b/kdb-bot/src/modules/admin/command/restart_command.py similarity index 100% rename from src/modules/admin/command/restart_command.py rename to kdb-bot/src/modules/admin/command/restart_command.py diff --git a/src/modules/admin/command/shutdown_command.py b/kdb-bot/src/modules/admin/command/shutdown_command.py similarity index 100% rename from src/modules/admin/command/shutdown_command.py rename to kdb-bot/src/modules/admin/command/shutdown_command.py diff --git a/src/modules/auto_role/__init__.py b/kdb-bot/src/modules/auto_role/__init__.py similarity index 100% rename from src/modules/auto_role/__init__.py rename to kdb-bot/src/modules/auto_role/__init__.py diff --git a/src/modules/auto_role/auto-role.json b/kdb-bot/src/modules/auto_role/auto-role.json similarity index 100% rename from src/modules/auto_role/auto-role.json rename to kdb-bot/src/modules/auto_role/auto-role.json diff --git a/src/modules/auto_role/auto_role_module.py b/kdb-bot/src/modules/auto_role/auto_role_module.py similarity index 100% rename from src/modules/auto_role/auto_role_module.py rename to kdb-bot/src/modules/auto_role/auto_role_module.py diff --git a/src/modules/auto_role/command/__init__.py b/kdb-bot/src/modules/auto_role/command/__init__.py similarity index 100% rename from src/modules/auto_role/command/__init__.py rename to kdb-bot/src/modules/auto_role/command/__init__.py diff --git a/src/modules/auto_role/command/auto_role_group.py b/kdb-bot/src/modules/auto_role/command/auto_role_group.py similarity index 100% rename from src/modules/auto_role/command/auto_role_group.py rename to kdb-bot/src/modules/auto_role/command/auto_role_group.py diff --git a/src/modules/auto_role/events/__init__.py b/kdb-bot/src/modules/auto_role/events/__init__.py similarity index 100% rename from src/modules/auto_role/events/__init__.py rename to kdb-bot/src/modules/auto_role/events/__init__.py diff --git a/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py similarity index 100% rename from src/modules/auto_role/events/auto_role_on_raw_reaction_add.py rename to kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py diff --git a/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py similarity index 100% rename from src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py rename to kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py diff --git a/src/modules/auto_role/helper/__init__.py b/kdb-bot/src/modules/auto_role/helper/__init__.py similarity index 100% rename from src/modules/auto_role/helper/__init__.py rename to kdb-bot/src/modules/auto_role/helper/__init__.py diff --git a/src/modules/auto_role/helper/reaction_handler.py b/kdb-bot/src/modules/auto_role/helper/reaction_handler.py similarity index 100% rename from src/modules/auto_role/helper/reaction_handler.py rename to kdb-bot/src/modules/auto_role/helper/reaction_handler.py diff --git a/src/modules/base/__init__.py b/kdb-bot/src/modules/base/__init__.py similarity index 100% rename from src/modules/base/__init__.py rename to kdb-bot/src/modules/base/__init__.py diff --git a/src/modules/base/abc/__init__.py b/kdb-bot/src/modules/base/abc/__init__.py similarity index 100% rename from src/modules/base/abc/__init__.py rename to kdb-bot/src/modules/base/abc/__init__.py diff --git a/src/modules/base/abc/base_helper_abc.py b/kdb-bot/src/modules/base/abc/base_helper_abc.py similarity index 100% rename from src/modules/base/abc/base_helper_abc.py rename to kdb-bot/src/modules/base/abc/base_helper_abc.py diff --git a/src/modules/base/base.json b/kdb-bot/src/modules/base/base.json similarity index 100% rename from src/modules/base/base.json rename to kdb-bot/src/modules/base/base.json diff --git a/src/modules/base/base_module.py b/kdb-bot/src/modules/base/base_module.py similarity index 100% rename from src/modules/base/base_module.py rename to kdb-bot/src/modules/base/base_module.py diff --git a/src/modules/base/command/__init__.py b/kdb-bot/src/modules/base/command/__init__.py similarity index 100% rename from src/modules/base/command/__init__.py rename to kdb-bot/src/modules/base/command/__init__.py diff --git a/src/modules/base/command/afk_command.py b/kdb-bot/src/modules/base/command/afk_command.py similarity index 100% rename from src/modules/base/command/afk_command.py rename to kdb-bot/src/modules/base/command/afk_command.py diff --git a/src/modules/base/command/help_command.py b/kdb-bot/src/modules/base/command/help_command.py similarity index 100% rename from src/modules/base/command/help_command.py rename to kdb-bot/src/modules/base/command/help_command.py diff --git a/src/modules/base/command/info_command.py b/kdb-bot/src/modules/base/command/info_command.py similarity index 100% rename from src/modules/base/command/info_command.py rename to kdb-bot/src/modules/base/command/info_command.py diff --git a/src/modules/base/command/ping_command.py b/kdb-bot/src/modules/base/command/ping_command.py similarity index 100% rename from src/modules/base/command/ping_command.py rename to kdb-bot/src/modules/base/command/ping_command.py diff --git a/src/modules/base/configuration/__init__.py b/kdb-bot/src/modules/base/configuration/__init__.py similarity index 100% rename from src/modules/base/configuration/__init__.py rename to kdb-bot/src/modules/base/configuration/__init__.py diff --git a/src/modules/base/configuration/base_server_settings.py b/kdb-bot/src/modules/base/configuration/base_server_settings.py similarity index 100% rename from src/modules/base/configuration/base_server_settings.py rename to kdb-bot/src/modules/base/configuration/base_server_settings.py diff --git a/src/modules/base/configuration/base_settings.py b/kdb-bot/src/modules/base/configuration/base_settings.py similarity index 100% rename from src/modules/base/configuration/base_settings.py rename to kdb-bot/src/modules/base/configuration/base_settings.py diff --git a/src/modules/base/events/__init__.py b/kdb-bot/src/modules/base/events/__init__.py similarity index 100% rename from src/modules/base/events/__init__.py rename to kdb-bot/src/modules/base/events/__init__.py diff --git a/src/modules/base/events/base_on_command_error_event.py b/kdb-bot/src/modules/base/events/base_on_command_error_event.py similarity index 100% rename from src/modules/base/events/base_on_command_error_event.py rename to kdb-bot/src/modules/base/events/base_on_command_error_event.py diff --git a/src/modules/base/events/base_on_command_event.py b/kdb-bot/src/modules/base/events/base_on_command_event.py similarity index 100% rename from src/modules/base/events/base_on_command_event.py rename to kdb-bot/src/modules/base/events/base_on_command_event.py diff --git a/src/modules/base/events/base_on_member_join_event.py b/kdb-bot/src/modules/base/events/base_on_member_join_event.py similarity index 100% rename from src/modules/base/events/base_on_member_join_event.py rename to kdb-bot/src/modules/base/events/base_on_member_join_event.py diff --git a/src/modules/base/events/base_on_member_remove_event.py b/kdb-bot/src/modules/base/events/base_on_member_remove_event.py similarity index 100% rename from src/modules/base/events/base_on_member_remove_event.py rename to kdb-bot/src/modules/base/events/base_on_member_remove_event.py diff --git a/src/modules/base/events/base_on_message_event.py b/kdb-bot/src/modules/base/events/base_on_message_event.py similarity index 100% rename from src/modules/base/events/base_on_message_event.py rename to kdb-bot/src/modules/base/events/base_on_message_event.py diff --git a/src/modules/base/events/base_on_voice_state_update_event.py b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py similarity index 100% rename from src/modules/base/events/base_on_voice_state_update_event.py rename to kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py diff --git a/src/modules/base/service/__init__.py b/kdb-bot/src/modules/base/service/__init__.py similarity index 100% rename from src/modules/base/service/__init__.py rename to kdb-bot/src/modules/base/service/__init__.py diff --git a/src/modules/base/service/base_helper_service.py b/kdb-bot/src/modules/base/service/base_helper_service.py similarity index 100% rename from src/modules/base/service/base_helper_service.py rename to kdb-bot/src/modules/base/service/base_helper_service.py diff --git a/src/modules/boot_log/__init__.py b/kdb-bot/src/modules/boot_log/__init__.py similarity index 100% rename from src/modules/boot_log/__init__.py rename to kdb-bot/src/modules/boot_log/__init__.py diff --git a/src/modules/boot_log/boot-log.json b/kdb-bot/src/modules/boot_log/boot-log.json similarity index 100% rename from src/modules/boot_log/boot-log.json rename to kdb-bot/src/modules/boot_log/boot-log.json diff --git a/src/modules/boot_log/boot_log_extension.py b/kdb-bot/src/modules/boot_log/boot_log_extension.py similarity index 100% rename from src/modules/boot_log/boot_log_extension.py rename to kdb-bot/src/modules/boot_log/boot_log_extension.py diff --git a/src/modules/boot_log/boot_log_module.py b/kdb-bot/src/modules/boot_log/boot_log_module.py similarity index 100% rename from src/modules/boot_log/boot_log_module.py rename to kdb-bot/src/modules/boot_log/boot_log_module.py diff --git a/src/modules/boot_log/boot_log_on_ready_event.py b/kdb-bot/src/modules/boot_log/boot_log_on_ready_event.py similarity index 100% rename from src/modules/boot_log/boot_log_on_ready_event.py rename to kdb-bot/src/modules/boot_log/boot_log_on_ready_event.py diff --git a/src/modules/boot_log/configuration/__init__.py b/kdb-bot/src/modules/boot_log/configuration/__init__.py similarity index 100% rename from src/modules/boot_log/configuration/__init__.py rename to kdb-bot/src/modules/boot_log/configuration/__init__.py diff --git a/src/modules/boot_log/configuration/boot_log_server_settings.py b/kdb-bot/src/modules/boot_log/configuration/boot_log_server_settings.py similarity index 100% rename from src/modules/boot_log/configuration/boot_log_server_settings.py rename to kdb-bot/src/modules/boot_log/configuration/boot_log_server_settings.py diff --git a/src/modules/boot_log/configuration/boot_log_settings.py b/kdb-bot/src/modules/boot_log/configuration/boot_log_settings.py similarity index 100% rename from src/modules/boot_log/configuration/boot_log_settings.py rename to kdb-bot/src/modules/boot_log/configuration/boot_log_settings.py diff --git a/src/modules/database/__init__.py b/kdb-bot/src/modules/database/__init__.py similarity index 100% rename from src/modules/database/__init__.py rename to kdb-bot/src/modules/database/__init__.py diff --git a/src/modules/database/database.json b/kdb-bot/src/modules/database/database.json similarity index 100% rename from src/modules/database/database.json rename to kdb-bot/src/modules/database/database.json diff --git a/src/modules/database/database_extension.py b/kdb-bot/src/modules/database/database_extension.py similarity index 100% rename from src/modules/database/database_extension.py rename to kdb-bot/src/modules/database/database_extension.py diff --git a/src/modules/database/database_module.py b/kdb-bot/src/modules/database/database_module.py similarity index 100% rename from src/modules/database/database_module.py rename to kdb-bot/src/modules/database/database_module.py diff --git a/src/modules/database/database_on_ready_event.py b/kdb-bot/src/modules/database/database_on_ready_event.py similarity index 100% rename from src/modules/database/database_on_ready_event.py rename to kdb-bot/src/modules/database/database_on_ready_event.py diff --git a/src/modules/moderator/__init__.py b/kdb-bot/src/modules/moderator/__init__.py similarity index 100% rename from src/modules/moderator/__init__.py rename to kdb-bot/src/modules/moderator/__init__.py diff --git a/src/modules/moderator/command/__init__.py b/kdb-bot/src/modules/moderator/command/__init__.py similarity index 100% rename from src/modules/moderator/command/__init__.py rename to kdb-bot/src/modules/moderator/command/__init__.py diff --git a/src/modules/moderator/command/purge_command.py b/kdb-bot/src/modules/moderator/command/purge_command.py similarity index 100% rename from src/modules/moderator/command/purge_command.py rename to kdb-bot/src/modules/moderator/command/purge_command.py diff --git a/src/modules/moderator/command/user_group.py b/kdb-bot/src/modules/moderator/command/user_group.py similarity index 100% rename from src/modules/moderator/command/user_group.py rename to kdb-bot/src/modules/moderator/command/user_group.py diff --git a/src/modules/moderator/moderator.json b/kdb-bot/src/modules/moderator/moderator.json similarity index 100% rename from src/modules/moderator/moderator.json rename to kdb-bot/src/modules/moderator/moderator.json diff --git a/src/modules/moderator/moderator_module.py b/kdb-bot/src/modules/moderator/moderator_module.py similarity index 100% rename from src/modules/moderator/moderator_module.py rename to kdb-bot/src/modules/moderator/moderator_module.py diff --git a/src/modules/permission/__init__.py b/kdb-bot/src/modules/permission/__init__.py similarity index 100% rename from src/modules/permission/__init__.py rename to kdb-bot/src/modules/permission/__init__.py diff --git a/src/modules/permission/abc/__init__.py b/kdb-bot/src/modules/permission/abc/__init__.py similarity index 100% rename from src/modules/permission/abc/__init__.py rename to kdb-bot/src/modules/permission/abc/__init__.py diff --git a/src/modules/permission/abc/permission_service_abc.py b/kdb-bot/src/modules/permission/abc/permission_service_abc.py similarity index 100% rename from src/modules/permission/abc/permission_service_abc.py rename to kdb-bot/src/modules/permission/abc/permission_service_abc.py diff --git a/src/modules/permission/configuration/__init__.py b/kdb-bot/src/modules/permission/configuration/__init__.py similarity index 100% rename from src/modules/permission/configuration/__init__.py rename to kdb-bot/src/modules/permission/configuration/__init__.py diff --git a/src/modules/permission/configuration/permission_server_settings.py b/kdb-bot/src/modules/permission/configuration/permission_server_settings.py similarity index 100% rename from src/modules/permission/configuration/permission_server_settings.py rename to kdb-bot/src/modules/permission/configuration/permission_server_settings.py diff --git a/src/modules/permission/configuration/permission_settings.py b/kdb-bot/src/modules/permission/configuration/permission_settings.py similarity index 100% rename from src/modules/permission/configuration/permission_settings.py rename to kdb-bot/src/modules/permission/configuration/permission_settings.py diff --git a/src/modules/permission/events/__init__.py b/kdb-bot/src/modules/permission/events/__init__.py similarity index 100% rename from src/modules/permission/events/__init__.py rename to kdb-bot/src/modules/permission/events/__init__.py diff --git a/src/modules/permission/events/permission_on_member_update_event.py b/kdb-bot/src/modules/permission/events/permission_on_member_update_event.py similarity index 100% rename from src/modules/permission/events/permission_on_member_update_event.py rename to kdb-bot/src/modules/permission/events/permission_on_member_update_event.py diff --git a/src/modules/permission/events/permission_on_ready_event.py b/kdb-bot/src/modules/permission/events/permission_on_ready_event.py similarity index 100% rename from src/modules/permission/events/permission_on_ready_event.py rename to kdb-bot/src/modules/permission/events/permission_on_ready_event.py diff --git a/src/modules/permission/permission.json b/kdb-bot/src/modules/permission/permission.json similarity index 100% rename from src/modules/permission/permission.json rename to kdb-bot/src/modules/permission/permission.json diff --git a/src/modules/permission/permission_module.py b/kdb-bot/src/modules/permission/permission_module.py similarity index 100% rename from src/modules/permission/permission_module.py rename to kdb-bot/src/modules/permission/permission_module.py diff --git a/src/modules/permission/service/__init__.py b/kdb-bot/src/modules/permission/service/__init__.py similarity index 100% rename from src/modules/permission/service/__init__.py rename to kdb-bot/src/modules/permission/service/__init__.py diff --git a/src/modules/permission/service/permission_service.py b/kdb-bot/src/modules/permission/service/permission_service.py similarity index 100% rename from src/modules/permission/service/permission_service.py rename to kdb-bot/src/modules/permission/service/permission_service.py From 2b08c1d71e04fd001d4a6181983d932426f31599 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 15 Oct 2022 23:38:38 +0200 Subject: [PATCH 023/275] Updated bot stuff #70 --- kdb-bot/src/bot/__init__.py | 4 ++-- kdb-bot/src/bot/bot.json | 12 +++++++++--- kdb-bot/src/bot_api/__init__.py | 4 ++-- kdb-bot/src/bot_api/abc/__init__.py | 4 ++-- kdb-bot/src/bot_api/bot-api.json | 8 +------- kdb-bot/src/bot_api/configuration/__init__.py | 4 ++-- kdb-bot/src/bot_api/controller/__init__.py | 4 ++-- kdb-bot/src/bot_api/exception/__init__.py | 4 ++-- kdb-bot/src/bot_api/filter/__init__.py | 4 ++-- kdb-bot/src/bot_api/logging/__init__.py | 4 ++-- kdb-bot/src/bot_api/model/__init__.py | 4 ++-- kdb-bot/src/bot_api/route/__init__.py | 4 ++-- kdb-bot/src/bot_api/service/__init__.py | 4 ++-- kdb-bot/src/bot_api/transformer/__init__.py | 4 ++-- kdb-bot/src/bot_core/__init__.py | 4 ++-- kdb-bot/src/bot_core/abc/__init__.py | 4 ++-- kdb-bot/src/bot_core/configuration/__init__.py | 4 ++-- kdb-bot/src/bot_core/core_extension/__init__.py | 4 ++-- kdb-bot/src/bot_core/events/__init__.py | 4 ++-- kdb-bot/src/bot_core/helper/__init__.py | 4 ++-- kdb-bot/src/bot_core/logging/__init__.py | 4 ++-- kdb-bot/src/bot_core/pipes/__init__.py | 4 ++-- kdb-bot/src/bot_core/service/__init__.py | 4 ++-- kdb-bot/src/bot_data/__init__.py | 4 ++-- kdb-bot/src/bot_data/abc/__init__.py | 4 ++-- kdb-bot/src/bot_data/migration/__init__.py | 4 ++-- kdb-bot/src/bot_data/model/__init__.py | 4 ++-- kdb-bot/src/bot_data/service/__init__.py | 4 ++-- kdb-bot/src/modules/admin/__init__.py | 4 ++-- kdb-bot/src/modules/admin/command/__init__.py | 4 ++-- kdb-bot/src/modules/auto_role/__init__.py | 4 ++-- kdb-bot/src/modules/auto_role/command/__init__.py | 4 ++-- kdb-bot/src/modules/auto_role/events/__init__.py | 4 ++-- kdb-bot/src/modules/auto_role/helper/__init__.py | 4 ++-- kdb-bot/src/modules/base/__init__.py | 4 ++-- kdb-bot/src/modules/base/abc/__init__.py | 4 ++-- kdb-bot/src/modules/base/command/__init__.py | 4 ++-- kdb-bot/src/modules/base/configuration/__init__.py | 4 ++-- kdb-bot/src/modules/base/events/__init__.py | 4 ++-- kdb-bot/src/modules/base/service/__init__.py | 4 ++-- kdb-bot/src/modules/boot_log/__init__.py | 4 ++-- .../src/modules/boot_log/configuration/__init__.py | 4 ++-- kdb-bot/src/modules/database/__init__.py | 4 ++-- kdb-bot/src/modules/moderator/__init__.py | 4 ++-- kdb-bot/src/modules/moderator/command/__init__.py | 4 ++-- kdb-bot/src/modules/permission/__init__.py | 4 ++-- kdb-bot/src/modules/permission/abc/__init__.py | 4 ++-- .../src/modules/permission/configuration/__init__.py | 4 ++-- kdb-bot/src/modules/permission/events/__init__.py | 4 ++-- kdb-bot/src/modules/permission/service/__init__.py | 4 ++-- 50 files changed, 106 insertions(+), 106 deletions(-) diff --git a/kdb-bot/src/bot/__init__.py b/kdb-bot/src/bot/__init__.py index 987fa6ee..bec166fa 100644 --- a/kdb-bot/src/bot/__init__.py +++ b/kdb-bot/src/bot/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot/bot.json b/kdb-bot/src/bot/bot.json index 89afddf9..ff36eea7 100644 --- a/kdb-bot/src/bot/bot.json +++ b/kdb-bot/src/bot/bot.json @@ -3,8 +3,8 @@ "Name": "bot", "Version": { "Major": "0", - "Minor": "2", - "Micro": "3" + "Minor": "3", + "Micro": "0" }, "Author": "Sven Heidemann", "AuthorEmail": "sven.heidemann@sh-edraft.de", @@ -19,7 +19,13 @@ "cpl-core==2022.10.0.post6", "cpl-translation==2022.10.0.post1", "cpl-query==2022.10.0.post2", - "cpl-discord==2022.10.0.post5" + "cpl-discord==2022.10.0.post5", + "Flask==2.2.2", + "Flask[async]==2.2.2", + "Flask-Classful==0.14.2", + "Flask-Cors==3.0.10", + "PyJWT[crypto]==2.5.0", + "PyJWT==2.5.0" ], "DevDependencies": [ "cpl-cli==2022.10.0" diff --git a/kdb-bot/src/bot_api/__init__.py b/kdb-bot/src/bot_api/__init__.py index 99097c73..6029ae84 100644 --- a/kdb-bot/src/bot_api/__init__.py +++ b/kdb-bot/src/bot_api/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_api/abc/__init__.py b/kdb-bot/src/bot_api/abc/__init__.py index 38bf68b7..25aa13fe 100644 --- a/kdb-bot/src/bot_api/abc/__init__.py +++ b/kdb-bot/src/bot_api/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.abc' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_api/bot-api.json b/kdb-bot/src/bot_api/bot-api.json index a807eeb1..86b1012e 100644 --- a/kdb-bot/src/bot_api/bot-api.json +++ b/kdb-bot/src/bot_api/bot-api.json @@ -16,13 +16,7 @@ "LicenseName": "", "LicenseDescription": "", "Dependencies": [ - "cpl-core==2022.10.0.post6", - "Flask==2.2.2", - "Flask[async]==2.2.2", - "Flask-Classful==0.14.2", - "Flask-Cors==3.0.10", - "PyJWT[crypto]==2.5.0", - "PyJWT==2.5.0" + "cpl-core==2022.10.0.post6" ], "DevDependencies": [ "cpl-cli==2022.10.0" diff --git a/kdb-bot/src/bot_api/configuration/__init__.py b/kdb-bot/src/bot_api/configuration/__init__.py index 86e07283..823f5a2b 100644 --- a/kdb-bot/src/bot_api/configuration/__init__.py +++ b/kdb-bot/src/bot_api/configuration/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.configuration' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_api/controller/__init__.py b/kdb-bot/src/bot_api/controller/__init__.py index 44c37e5c..e7085405 100644 --- a/kdb-bot/src/bot_api/controller/__init__.py +++ b/kdb-bot/src/bot_api/controller/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.controller' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_api/exception/__init__.py b/kdb-bot/src/bot_api/exception/__init__.py index b37a52c4..4db4fc25 100644 --- a/kdb-bot/src/bot_api/exception/__init__.py +++ b/kdb-bot/src/bot_api/exception/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.exception' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_api/filter/__init__.py b/kdb-bot/src/bot_api/filter/__init__.py index 0f9e52aa..0b898bd2 100644 --- a/kdb-bot/src/bot_api/filter/__init__.py +++ b/kdb-bot/src/bot_api/filter/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.filter' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_api/logging/__init__.py b/kdb-bot/src/bot_api/logging/__init__.py index d7b9d180..7575e633 100644 --- a/kdb-bot/src/bot_api/logging/__init__.py +++ b/kdb-bot/src/bot_api/logging/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.logging' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_api/model/__init__.py b/kdb-bot/src/bot_api/model/__init__.py index da73f223..47a3f826 100644 --- a/kdb-bot/src/bot_api/model/__init__.py +++ b/kdb-bot/src/bot_api/model/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.model' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_api/route/__init__.py b/kdb-bot/src/bot_api/route/__init__.py index 3f08538e..7225cfad 100644 --- a/kdb-bot/src/bot_api/route/__init__.py +++ b/kdb-bot/src/bot_api/route/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.route' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_api/service/__init__.py b/kdb-bot/src/bot_api/service/__init__.py index 3a5cf24d..1e2fd6a5 100644 --- a/kdb-bot/src/bot_api/service/__init__.py +++ b/kdb-bot/src/bot_api/service/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_api/transformer/__init__.py b/kdb-bot/src/bot_api/transformer/__init__.py index 4c614cb9..a1915e9f 100644 --- a/kdb-bot/src/bot_api/transformer/__init__.py +++ b/kdb-bot/src/bot_api/transformer/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.transformer' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_core/__init__.py b/kdb-bot/src/bot_core/__init__.py index 75e45f3f..9d27fa55 100644 --- a/kdb-bot/src/bot_core/__init__.py +++ b/kdb-bot/src/bot_core/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_core/abc/__init__.py b/kdb-bot/src/bot_core/abc/__init__.py index 6bed6e11..e8949ac9 100644 --- a/kdb-bot/src/bot_core/abc/__init__.py +++ b/kdb-bot/src/bot_core/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.abc' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_core/configuration/__init__.py b/kdb-bot/src/bot_core/configuration/__init__.py index 0e6f2e1a..3e2d80ff 100644 --- a/kdb-bot/src/bot_core/configuration/__init__.py +++ b/kdb-bot/src/bot_core/configuration/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.configuration' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_core/core_extension/__init__.py b/kdb-bot/src/bot_core/core_extension/__init__.py index 201573f4..f6d56342 100644 --- a/kdb-bot/src/bot_core/core_extension/__init__.py +++ b/kdb-bot/src/bot_core/core_extension/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.core_extension' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_core/events/__init__.py b/kdb-bot/src/bot_core/events/__init__.py index 278da61a..3ea3777d 100644 --- a/kdb-bot/src/bot_core/events/__init__.py +++ b/kdb-bot/src/bot_core/events/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.events' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_core/helper/__init__.py b/kdb-bot/src/bot_core/helper/__init__.py index bfd7ff19..e6122ab2 100644 --- a/kdb-bot/src/bot_core/helper/__init__.py +++ b/kdb-bot/src/bot_core/helper/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.helper' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_core/logging/__init__.py b/kdb-bot/src/bot_core/logging/__init__.py index 5ae285bb..7c415dff 100644 --- a/kdb-bot/src/bot_core/logging/__init__.py +++ b/kdb-bot/src/bot_core/logging/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.logging' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_core/pipes/__init__.py b/kdb-bot/src/bot_core/pipes/__init__.py index e9f62709..345eea8d 100644 --- a/kdb-bot/src/bot_core/pipes/__init__.py +++ b/kdb-bot/src/bot_core/pipes/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.pipes' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_core/service/__init__.py b/kdb-bot/src/bot_core/service/__init__.py index 63cc8d89..2e659d24 100644 --- a/kdb-bot/src/bot_core/service/__init__.py +++ b/kdb-bot/src/bot_core/service/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_data/__init__.py b/kdb-bot/src/bot_data/__init__.py index 0e69edf4..6ac4b954 100644 --- a/kdb-bot/src/bot_data/__init__.py +++ b/kdb-bot/src/bot_data/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_data' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_data/abc/__init__.py b/kdb-bot/src/bot_data/abc/__init__.py index 8a57f0d0..e312030c 100644 --- a/kdb-bot/src/bot_data/abc/__init__.py +++ b/kdb-bot/src/bot_data/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_data.abc' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_data/migration/__init__.py b/kdb-bot/src/bot_data/migration/__init__.py index d28a56ee..b8443711 100644 --- a/kdb-bot/src/bot_data/migration/__init__.py +++ b/kdb-bot/src/bot_data/migration/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_data.migration' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_data/model/__init__.py b/kdb-bot/src/bot_data/model/__init__.py index a66634fd..ba35160c 100644 --- a/kdb-bot/src/bot_data/model/__init__.py +++ b/kdb-bot/src/bot_data/model/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_data.model' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/bot_data/service/__init__.py b/kdb-bot/src/bot_data/service/__init__.py index 69c25125..6ed37769 100644 --- a/kdb-bot/src/bot_data/service/__init__.py +++ b/kdb-bot/src/bot_data/service/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_data.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/admin/__init__.py b/kdb-bot/src/modules/admin/__init__.py index bbf3f2b0..fa637ebc 100644 --- a/kdb-bot/src/modules/admin/__init__.py +++ b/kdb-bot/src/modules/admin/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.admin' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/admin/command/__init__.py b/kdb-bot/src/modules/admin/command/__init__.py index c5035ad0..4ca82ff5 100644 --- a/kdb-bot/src/modules/admin/command/__init__.py +++ b/kdb-bot/src/modules/admin/command/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.admin.command' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/auto_role/__init__.py b/kdb-bot/src/modules/auto_role/__init__.py index 6b3e526e..bb9a5a5f 100644 --- a/kdb-bot/src/modules/auto_role/__init__.py +++ b/kdb-bot/src/modules/auto_role/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.auto_role' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/auto_role/command/__init__.py b/kdb-bot/src/modules/auto_role/command/__init__.py index 9e9cc1ae..580fe9f1 100644 --- a/kdb-bot/src/modules/auto_role/command/__init__.py +++ b/kdb-bot/src/modules/auto_role/command/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.auto_role.command' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/auto_role/events/__init__.py b/kdb-bot/src/modules/auto_role/events/__init__.py index f431645b..727f90b8 100644 --- a/kdb-bot/src/modules/auto_role/events/__init__.py +++ b/kdb-bot/src/modules/auto_role/events/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.auto_role.events' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/auto_role/helper/__init__.py b/kdb-bot/src/modules/auto_role/helper/__init__.py index d90d3bab..c0d58dcd 100644 --- a/kdb-bot/src/modules/auto_role/helper/__init__.py +++ b/kdb-bot/src/modules/auto_role/helper/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.auto_role.helper' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/base/__init__.py b/kdb-bot/src/modules/base/__init__.py index 337ba2d6..e3cb6607 100644 --- a/kdb-bot/src/modules/base/__init__.py +++ b/kdb-bot/src/modules/base/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.base' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/base/abc/__init__.py b/kdb-bot/src/modules/base/abc/__init__.py index 4396c0e5..a5b10a34 100644 --- a/kdb-bot/src/modules/base/abc/__init__.py +++ b/kdb-bot/src/modules/base/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.base.abc' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/base/command/__init__.py b/kdb-bot/src/modules/base/command/__init__.py index 7383ff78..4ba0eba9 100644 --- a/kdb-bot/src/modules/base/command/__init__.py +++ b/kdb-bot/src/modules/base/command/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.base.command' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/base/configuration/__init__.py b/kdb-bot/src/modules/base/configuration/__init__.py index bf7d4c47..bd2cc24d 100644 --- a/kdb-bot/src/modules/base/configuration/__init__.py +++ b/kdb-bot/src/modules/base/configuration/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.base.configuration' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/base/events/__init__.py b/kdb-bot/src/modules/base/events/__init__.py index 3337e0d9..04f7a371 100644 --- a/kdb-bot/src/modules/base/events/__init__.py +++ b/kdb-bot/src/modules/base/events/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.base.events' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/base/service/__init__.py b/kdb-bot/src/modules/base/service/__init__.py index 37d31ea0..92e0c816 100644 --- a/kdb-bot/src/modules/base/service/__init__.py +++ b/kdb-bot/src/modules/base/service/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.base.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/boot_log/__init__.py b/kdb-bot/src/modules/boot_log/__init__.py index a156bb4f..e5cc21aa 100644 --- a/kdb-bot/src/modules/boot_log/__init__.py +++ b/kdb-bot/src/modules/boot_log/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.boot_log' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/boot_log/configuration/__init__.py b/kdb-bot/src/modules/boot_log/configuration/__init__.py index 8d1ce4fa..0d76108e 100644 --- a/kdb-bot/src/modules/boot_log/configuration/__init__.py +++ b/kdb-bot/src/modules/boot_log/configuration/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.boot_log.configuration' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/database/__init__.py b/kdb-bot/src/modules/database/__init__.py index 694778b0..0615d2a1 100644 --- a/kdb-bot/src/modules/database/__init__.py +++ b/kdb-bot/src/modules/database/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.database' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/moderator/__init__.py b/kdb-bot/src/modules/moderator/__init__.py index 5416853f..1b8e00d3 100644 --- a/kdb-bot/src/modules/moderator/__init__.py +++ b/kdb-bot/src/modules/moderator/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.moderator' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/moderator/command/__init__.py b/kdb-bot/src/modules/moderator/command/__init__.py index beeffcd4..e392b8ea 100644 --- a/kdb-bot/src/modules/moderator/command/__init__.py +++ b/kdb-bot/src/modules/moderator/command/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.moderator.command' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/permission/__init__.py b/kdb-bot/src/modules/permission/__init__.py index 51190b7a..f14bcd24 100644 --- a/kdb-bot/src/modules/permission/__init__.py +++ b/kdb-bot/src/modules/permission/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.permission' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/permission/abc/__init__.py b/kdb-bot/src/modules/permission/abc/__init__.py index 28a1c61e..220dde0d 100644 --- a/kdb-bot/src/modules/permission/abc/__init__.py +++ b/kdb-bot/src/modules/permission/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.permission.abc' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/permission/configuration/__init__.py b/kdb-bot/src/modules/permission/configuration/__init__.py index 4ad1f1d4..1de58813 100644 --- a/kdb-bot/src/modules/permission/configuration/__init__.py +++ b/kdb-bot/src/modules/permission/configuration/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.permission.configuration' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/permission/events/__init__.py b/kdb-bot/src/modules/permission/events/__init__.py index 6dbd86c1..117b3b8b 100644 --- a/kdb-bot/src/modules/permission/events/__init__.py +++ b/kdb-bot/src/modules/permission/events/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.permission.events' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') diff --git a/kdb-bot/src/modules/permission/service/__init__.py b/kdb-bot/src/modules/permission/service/__init__.py index 1341ec8d..8d6d28f1 100644 --- a/kdb-bot/src/modules/permission/service/__init__.py +++ b/kdb-bot/src/modules/permission/service/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.permission.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.2.3' +__version__ = '0.3.0' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='2', micro='3') +version_info = VersionInfo(major='0', minor='3', micro='0') From ee49bde9612518370714d6270e841e9610d4a1f0 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 15 Oct 2022 23:48:38 +0200 Subject: [PATCH 024/275] Added waitress dep #70 --- kdb-bot/src/bot/bot.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kdb-bot/src/bot/bot.json b/kdb-bot/src/bot/bot.json index ff36eea7..799d3fe8 100644 --- a/kdb-bot/src/bot/bot.json +++ b/kdb-bot/src/bot/bot.json @@ -21,11 +21,10 @@ "cpl-query==2022.10.0.post2", "cpl-discord==2022.10.0.post5", "Flask==2.2.2", - "Flask[async]==2.2.2", "Flask-Classful==0.14.2", "Flask-Cors==3.0.10", - "PyJWT[crypto]==2.5.0", - "PyJWT==2.5.0" + "PyJWT==2.5.0", + "waitress==2.1.2" ], "DevDependencies": [ "cpl-cli==2022.10.0" From 8c3cd1fae7fc58550fb169f478fdb4c049d3e6b8 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 00:13:03 +0200 Subject: [PATCH 025/275] Added angular frontend #70 --- kdb-web/.browserslistrc | 16 + kdb-web/.editorconfig | 16 + kdb-web/.gitignore | 43 + kdb-web/README.md | 27 + kdb-web/angular.json | 105 + kdb-web/karma.conf.js | 44 + kdb-web/package-lock.json | 21484 ++++++++++++++++ kdb-web/package.json | 49 + kdb-web/src/app/app-routing.module.ts | 22 + kdb-web/src/app/app.component.html | 36 + kdb-web/src/app/app.component.scss | 0 kdb-web/src/app/app.component.spec.ts | 16 + kdb-web/src/app/app.component.ts | 21 + kdb-web/src/app/app.module.ts | 81 + .../error/not-found/not-found.component.html | 15 + .../error/not-found/not-found.component.scss | 0 .../not-found/not-found.component.spec.ts | 25 + .../error/not-found/not-found.component.ts | 18 + .../components/footer/footer.component.html | 26 + .../components/footer/footer.component.scss | 0 .../footer/footer.component.spec.ts | 25 + .../app/components/footer/footer.component.ts | 46 + .../components/header/header.component.html | 25 + .../components/header/header.component.scss | 0 .../header/header.component.spec.ts | 25 + .../app/components/header/header.component.ts | 168 + .../components/sidebar/sidebar.component.html | 3 + .../components/sidebar/sidebar.component.scss | 0 .../sidebar/sidebar.component.spec.ts | 25 + .../components/sidebar/sidebar.component.ts | 52 + .../components/spinner/spinner.component.html | 7 + .../components/spinner/spinner.component.scss | 0 .../spinner/spinner.component.spec.ts | 25 + .../components/spinner/spinner.component.ts | 18 + .../app/models/auth/admin-update-user.dto.ts | 7 + .../models/auth/auth-error-messages.enum.ts | 7 + .../src/app/models/auth/auth-roles.enum.ts | 4 + .../app/models/auth/auth-user-atr-errors.ts | 12 + kdb-web/src/app/models/auth/auth-user.dto.ts | 11 + .../src/app/models/auth/email-string.dto.ts | 3 + .../auth/register-error-messages.enum.ts | 4 + .../src/app/models/auth/reset-password.dto.ts | 6 + kdb-web/src/app/models/auth/token.dto.ts | 4 + .../src/app/models/auth/update-user.dto.ts | 6 + kdb-web/src/app/models/config/api-version.ts | 5 + kdb-web/src/app/models/config/appsettings.ts | 8 + kdb-web/src/app/models/config/settings.dto.ts | 16 + .../app/models/config/software-version.dto.ts | 5 + .../src/app/models/config/software-version.ts | 19 + kdb-web/src/app/models/error/error-dto.ts | 8 + .../models/error/service-error-code.enum.ts | 16 + .../auth-user-select-criterion.dto.ts | 8 + .../get-filtered-auth-users-result.dto.ts | 6 + .../logins/login-select-criterion.dto.ts | 8 + .../selection/select-criterion.model.ts | 6 + .../app/models/utils/confirmation-dialog.ts | 7 + kdb-web/src/app/models/utils/toast-options.ts | 5 + kdb-web/src/app/models/view/theme.ts | 4 + kdb-web/src/app/models/view/themes.enum.ts | 7 + .../auth-users/auth-user-routing.module.ts | 13 + .../admin/auth-users/auth-user.module.ts | 18 + .../auth-user/auth-user.component.html | 201 + .../auth-user/auth-user.component.scss | 0 .../auth-user/auth-user.component.spec.ts | 25 + .../auth-user/auth-user.component.ts | 330 + .../settings/settings.component.html | 125 + .../settings/settings.component.scss | 0 .../settings/settings.component.spec.ts | 25 + .../components/settings/settings.component.ts | 121 + .../admin/settings/settings-routing.module.ts | 13 + .../modules/admin/settings/settings.module.ts | 19 + .../app/modules/auth/auth-routing.module.ts | 21 + kdb-web/src/app/modules/auth/auth.module.ts | 23 + .../forget-password.component.html | 57 + .../forget-password.component.scss | 0 .../forget-password.component.spec.ts | 25 + .../forget-password.component.ts | 136 + .../components/login/login.component.html | 58 + .../components/login/login.component.scss | 0 .../components/login/login.component.spec.ts | 25 + .../auth/components/login/login.component.ts | 114 + .../registration/registration.component.html | 83 + .../registration/registration.component.scss | 0 .../registration.component.spec.ts | 25 + .../registration/registration.component.ts | 144 + .../shared/guards/auth/auth.guard.spec.ts | 16 + .../modules/shared/guards/auth/auth.guard.ts | 38 + .../modules/shared/pipes/auth-role.pipe.ts | 13 + .../modules/shared/pipes/bool.pipe.spec.ts | 8 + .../src/app/modules/shared/pipes/bool.pipe.ts | 21 + .../modules/shared/pipes/ip-address.pipe.ts | 27 + .../src/app/modules/shared/shared.module.ts | 71 + .../change-password-routing.module.ts | 13 + .../change-password/change-password.module.ts | 19 + .../change-password.component.html | 35 + .../change-password.component.scss | 0 .../change-password.component.spec.ts | 25 + .../change-password.component.ts | 107 + .../dashboard/dashboard.component.html | 3 + .../dashboard/dashboard.component.scss | 0 .../dashboard/dashboard.component.spec.ts | 25 + .../dashboard/dashboard.component.ts | 14 + .../dashboard/dashboard-routing.module.ts | 13 + .../view/dashboard/dashboard.module.ts | 19 + .../user-settings.component.html | 41 + .../user-settings.component.scss | 0 .../user-settings.component.spec.ts | 25 + .../user-settings/user-settings.component.ts | 151 + .../user-settings-routing.module.ts | 13 + .../user-settings/user-settings.module.ts | 19 + .../app/services/auth/auth.service.spec.ts | 16 + kdb-web/src/app/services/auth/auth.service.ts | 243 + .../confirmation-dialog.service.spec.ts | 16 + .../confirmation-dialog.service.ts | 53 + .../app/services/data/data.service.spec.ts | 16 + kdb-web/src/app/services/data/data.service.ts | 14 + .../error-handler.service.spec.ts | 16 + .../error-handler/error-handler.service.ts | 34 + .../src/app/services/gui/gui.service.spec.ts | 16 + kdb-web/src/app/services/gui/gui.service.ts | 41 + .../settings/settings.service.spec.ts | 16 + .../app/services/settings/settings.service.ts | 62 + .../services/spinner/spinner.service.spec.ts | 16 + .../app/services/spinner/spinner.service.ts | 22 + .../app/services/theme/theme.service.spec.ts | 16 + .../src/app/services/theme/theme.service.ts | 98 + .../app/services/toast/toast.service.spec.ts | 16 + .../src/app/services/toast/toast.service.ts | 40 + kdb-web/src/assets/.gitkeep | 0 kdb-web/src/assets/config.json | 26 + kdb-web/src/assets/i18n/de.json | 258 + kdb-web/src/assets/i18n/en.json | 322 + kdb-web/src/assets/images/favicon.ico | Bin 0 -> 948 bytes kdb-web/src/environments/environment.prod.ts | 3 + kdb-web/src/environments/environment.ts | 16 + kdb-web/src/favicon.ico | Bin 0 -> 948 bytes kdb-web/src/index.html | 13 + kdb-web/src/main.ts | 12 + kdb-web/src/polyfills.ts | 53 + kdb-web/src/styles.scss | 394 + kdb-web/src/styles/constants.scss | 2 + kdb-web/src/styles/primeng-fixes.scss | 71 + .../src/styles/themes/default-dark-theme.scss | 517 + .../styles/themes/default-light-theme.scss | 515 + .../styles/themes/sh-edraft-dark-theme.scss | 519 + .../styles/themes/sh-edraft-light-theme.scss | 515 + kdb-web/src/test.ts | 26 + kdb-web/tsconfig.app.json | 15 + kdb-web/tsconfig.json | 32 + kdb-web/tsconfig.spec.json | 18 + kdb-web/update-version.ts | 54 + 151 files changed, 29104 insertions(+) create mode 100644 kdb-web/.browserslistrc create mode 100644 kdb-web/.editorconfig create mode 100644 kdb-web/.gitignore create mode 100644 kdb-web/README.md create mode 100644 kdb-web/angular.json create mode 100644 kdb-web/karma.conf.js create mode 100644 kdb-web/package-lock.json create mode 100644 kdb-web/package.json create mode 100644 kdb-web/src/app/app-routing.module.ts create mode 100644 kdb-web/src/app/app.component.html create mode 100644 kdb-web/src/app/app.component.scss create mode 100644 kdb-web/src/app/app.component.spec.ts create mode 100644 kdb-web/src/app/app.component.ts create mode 100644 kdb-web/src/app/app.module.ts create mode 100644 kdb-web/src/app/components/error/not-found/not-found.component.html create mode 100644 kdb-web/src/app/components/error/not-found/not-found.component.scss create mode 100644 kdb-web/src/app/components/error/not-found/not-found.component.spec.ts create mode 100644 kdb-web/src/app/components/error/not-found/not-found.component.ts create mode 100644 kdb-web/src/app/components/footer/footer.component.html create mode 100644 kdb-web/src/app/components/footer/footer.component.scss create mode 100644 kdb-web/src/app/components/footer/footer.component.spec.ts create mode 100644 kdb-web/src/app/components/footer/footer.component.ts create mode 100644 kdb-web/src/app/components/header/header.component.html create mode 100644 kdb-web/src/app/components/header/header.component.scss create mode 100644 kdb-web/src/app/components/header/header.component.spec.ts create mode 100644 kdb-web/src/app/components/header/header.component.ts create mode 100644 kdb-web/src/app/components/sidebar/sidebar.component.html create mode 100644 kdb-web/src/app/components/sidebar/sidebar.component.scss create mode 100644 kdb-web/src/app/components/sidebar/sidebar.component.spec.ts create mode 100644 kdb-web/src/app/components/sidebar/sidebar.component.ts create mode 100644 kdb-web/src/app/components/spinner/spinner.component.html create mode 100644 kdb-web/src/app/components/spinner/spinner.component.scss create mode 100644 kdb-web/src/app/components/spinner/spinner.component.spec.ts create mode 100644 kdb-web/src/app/components/spinner/spinner.component.ts create mode 100644 kdb-web/src/app/models/auth/admin-update-user.dto.ts create mode 100644 kdb-web/src/app/models/auth/auth-error-messages.enum.ts create mode 100644 kdb-web/src/app/models/auth/auth-roles.enum.ts create mode 100644 kdb-web/src/app/models/auth/auth-user-atr-errors.ts create mode 100644 kdb-web/src/app/models/auth/auth-user.dto.ts create mode 100644 kdb-web/src/app/models/auth/email-string.dto.ts create mode 100644 kdb-web/src/app/models/auth/register-error-messages.enum.ts create mode 100644 kdb-web/src/app/models/auth/reset-password.dto.ts create mode 100644 kdb-web/src/app/models/auth/token.dto.ts create mode 100644 kdb-web/src/app/models/auth/update-user.dto.ts create mode 100644 kdb-web/src/app/models/config/api-version.ts create mode 100644 kdb-web/src/app/models/config/appsettings.ts create mode 100644 kdb-web/src/app/models/config/settings.dto.ts create mode 100644 kdb-web/src/app/models/config/software-version.dto.ts create mode 100644 kdb-web/src/app/models/config/software-version.ts create mode 100644 kdb-web/src/app/models/error/error-dto.ts create mode 100644 kdb-web/src/app/models/error/service-error-code.enum.ts create mode 100644 kdb-web/src/app/models/selection/auth-user/auth-user-select-criterion.dto.ts create mode 100644 kdb-web/src/app/models/selection/auth-user/get-filtered-auth-users-result.dto.ts create mode 100644 kdb-web/src/app/models/selection/logins/login-select-criterion.dto.ts create mode 100644 kdb-web/src/app/models/selection/select-criterion.model.ts create mode 100644 kdb-web/src/app/models/utils/confirmation-dialog.ts create mode 100644 kdb-web/src/app/models/utils/toast-options.ts create mode 100644 kdb-web/src/app/models/view/theme.ts create mode 100644 kdb-web/src/app/models/view/themes.enum.ts create mode 100644 kdb-web/src/app/modules/admin/auth-users/auth-user-routing.module.ts create mode 100644 kdb-web/src/app/modules/admin/auth-users/auth-user.module.ts create mode 100644 kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html create mode 100644 kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.scss create mode 100644 kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.spec.ts create mode 100644 kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts create mode 100644 kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html create mode 100644 kdb-web/src/app/modules/admin/settings/components/settings/settings.component.scss create mode 100644 kdb-web/src/app/modules/admin/settings/components/settings/settings.component.spec.ts create mode 100644 kdb-web/src/app/modules/admin/settings/components/settings/settings.component.ts create mode 100644 kdb-web/src/app/modules/admin/settings/settings-routing.module.ts create mode 100644 kdb-web/src/app/modules/admin/settings/settings.module.ts create mode 100644 kdb-web/src/app/modules/auth/auth-routing.module.ts create mode 100644 kdb-web/src/app/modules/auth/auth.module.ts create mode 100644 kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.html create mode 100644 kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.scss create mode 100644 kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.spec.ts create mode 100644 kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.ts create mode 100644 kdb-web/src/app/modules/auth/components/login/login.component.html create mode 100644 kdb-web/src/app/modules/auth/components/login/login.component.scss create mode 100644 kdb-web/src/app/modules/auth/components/login/login.component.spec.ts create mode 100644 kdb-web/src/app/modules/auth/components/login/login.component.ts create mode 100644 kdb-web/src/app/modules/auth/components/registration/registration.component.html create mode 100644 kdb-web/src/app/modules/auth/components/registration/registration.component.scss create mode 100644 kdb-web/src/app/modules/auth/components/registration/registration.component.spec.ts create mode 100644 kdb-web/src/app/modules/auth/components/registration/registration.component.ts create mode 100644 kdb-web/src/app/modules/shared/guards/auth/auth.guard.spec.ts create mode 100644 kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts create mode 100644 kdb-web/src/app/modules/shared/pipes/auth-role.pipe.ts create mode 100644 kdb-web/src/app/modules/shared/pipes/bool.pipe.spec.ts create mode 100644 kdb-web/src/app/modules/shared/pipes/bool.pipe.ts create mode 100644 kdb-web/src/app/modules/shared/pipes/ip-address.pipe.ts create mode 100644 kdb-web/src/app/modules/shared/shared.module.ts create mode 100644 kdb-web/src/app/modules/view/change-password/change-password-routing.module.ts create mode 100644 kdb-web/src/app/modules/view/change-password/change-password.module.ts create mode 100644 kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.html create mode 100644 kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.scss create mode 100644 kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.spec.ts create mode 100644 kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.ts create mode 100644 kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html create mode 100644 kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.scss create mode 100644 kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.spec.ts create mode 100644 kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts create mode 100644 kdb-web/src/app/modules/view/dashboard/dashboard-routing.module.ts create mode 100644 kdb-web/src/app/modules/view/dashboard/dashboard.module.ts create mode 100644 kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.html create mode 100644 kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.scss create mode 100644 kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.spec.ts create mode 100644 kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.ts create mode 100644 kdb-web/src/app/modules/view/user-settings/user-settings-routing.module.ts create mode 100644 kdb-web/src/app/modules/view/user-settings/user-settings.module.ts create mode 100644 kdb-web/src/app/services/auth/auth.service.spec.ts create mode 100644 kdb-web/src/app/services/auth/auth.service.ts create mode 100644 kdb-web/src/app/services/confirmation-dialog/confirmation-dialog.service.spec.ts create mode 100644 kdb-web/src/app/services/confirmation-dialog/confirmation-dialog.service.ts create mode 100644 kdb-web/src/app/services/data/data.service.spec.ts create mode 100644 kdb-web/src/app/services/data/data.service.ts create mode 100644 kdb-web/src/app/services/error-handler/error-handler.service.spec.ts create mode 100644 kdb-web/src/app/services/error-handler/error-handler.service.ts create mode 100644 kdb-web/src/app/services/gui/gui.service.spec.ts create mode 100644 kdb-web/src/app/services/gui/gui.service.ts create mode 100644 kdb-web/src/app/services/settings/settings.service.spec.ts create mode 100644 kdb-web/src/app/services/settings/settings.service.ts create mode 100644 kdb-web/src/app/services/spinner/spinner.service.spec.ts create mode 100644 kdb-web/src/app/services/spinner/spinner.service.ts create mode 100644 kdb-web/src/app/services/theme/theme.service.spec.ts create mode 100644 kdb-web/src/app/services/theme/theme.service.ts create mode 100644 kdb-web/src/app/services/toast/toast.service.spec.ts create mode 100644 kdb-web/src/app/services/toast/toast.service.ts create mode 100644 kdb-web/src/assets/.gitkeep create mode 100644 kdb-web/src/assets/config.json create mode 100644 kdb-web/src/assets/i18n/de.json create mode 100644 kdb-web/src/assets/i18n/en.json create mode 100644 kdb-web/src/assets/images/favicon.ico create mode 100644 kdb-web/src/environments/environment.prod.ts create mode 100644 kdb-web/src/environments/environment.ts create mode 100644 kdb-web/src/favicon.ico create mode 100644 kdb-web/src/index.html create mode 100644 kdb-web/src/main.ts create mode 100644 kdb-web/src/polyfills.ts create mode 100644 kdb-web/src/styles.scss create mode 100644 kdb-web/src/styles/constants.scss create mode 100644 kdb-web/src/styles/primeng-fixes.scss create mode 100644 kdb-web/src/styles/themes/default-dark-theme.scss create mode 100644 kdb-web/src/styles/themes/default-light-theme.scss create mode 100644 kdb-web/src/styles/themes/sh-edraft-dark-theme.scss create mode 100644 kdb-web/src/styles/themes/sh-edraft-light-theme.scss create mode 100644 kdb-web/src/test.ts create mode 100644 kdb-web/tsconfig.app.json create mode 100644 kdb-web/tsconfig.json create mode 100644 kdb-web/tsconfig.spec.json create mode 100644 kdb-web/update-version.ts diff --git a/kdb-web/.browserslistrc b/kdb-web/.browserslistrc new file mode 100644 index 00000000..4f9ac269 --- /dev/null +++ b/kdb-web/.browserslistrc @@ -0,0 +1,16 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# For the full list of supported browsers by the Angular framework, please see: +# https://angular.io/guide/browser-support + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +last 1 Chrome version +last 1 Firefox version +last 2 Edge major versions +last 2 Safari major versions +last 2 iOS major versions +Firefox ESR diff --git a/kdb-web/.editorconfig b/kdb-web/.editorconfig new file mode 100644 index 00000000..59d9a3a3 --- /dev/null +++ b/kdb-web/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/kdb-web/.gitignore b/kdb-web/.gitignore new file mode 100644 index 00000000..c03363f8 --- /dev/null +++ b/kdb-web/.gitignore @@ -0,0 +1,43 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +.vscode +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files +.DS_Store +Thumbs.db diff --git a/kdb-web/README.md b/kdb-web/README.md new file mode 100644 index 00000000..74406fd5 --- /dev/null +++ b/kdb-web/README.md @@ -0,0 +1,27 @@ +# KdbWeb + +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.0. + +## Development server + +Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. + +## Code scaffolding + +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. + +## Build + +Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Running unit tests + +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Running end-to-end tests + +Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. diff --git a/kdb-web/angular.json b/kdb-web/angular.json new file mode 100644 index 00000000..ee3ffca4 --- /dev/null +++ b/kdb-web/angular.json @@ -0,0 +1,105 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "kdb-web": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + }, + "@schematics/angular:application": { + "strict": true + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/kdb-web", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": ["src/favicon.ico", "src/assets"], + "styles": [ + "src/styles.scss", + "node_modules/primeicons/primeicons.css", + "node_modules/primeng/resources/themes/lara-light-blue/theme.css", + "node_modules/primeng/resources/primeng.min.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "browserTarget": "kdb-web:build:production" + }, + "development": { + "browserTarget": "kdb-web:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "kdb-web:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.spec.json", + "karmaConfig": "karma.conf.js", + "inlineStyleLanguage": "scss", + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.scss"], + "scripts": [] + } + } + } + } + } +} diff --git a/kdb-web/karma.conf.js b/kdb-web/karma.conf.js new file mode 100644 index 00000000..18712757 --- /dev/null +++ b/kdb-web/karma.conf.js @@ -0,0 +1,44 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, './coverage/kdb-web'), + subdir: '.', + reporters: [ + { type: 'html' }, + { type: 'text-summary' } + ] + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/kdb-web/package-lock.json b/kdb-web/package-lock.json new file mode 100644 index 00000000..46d080cf --- /dev/null +++ b/kdb-web/package-lock.json @@ -0,0 +1,21484 @@ +{ + "name": "kdb-web", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "kdb-web", + "version": "0.0.0", + "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", + "@auth0/angular-jwt": "^5.1.0", + "@microsoft/signalr": "^6.0.9", + "@ngx-translate/core": "^14.0.0", + "@ngx-translate/http-loader": "^7.0.0", + "primeicons": "^6.0.1", + "primeng": "^14.1.2", + "rxjs": "~7.5.0", + "tslib": "^2.3.0", + "zone.js": "~0.11.4" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^14.0.0", + "@angular/cli": "~14.0.0", + "@angular/compiler-cli": "^14.0.0", + "@types/jasmine": "~4.0.0", + "@types/node": "^18.8.3", + "jasmine-core": "~4.1.0", + "karma": "~6.3.0", + "karma-chrome-launcher": "~3.1.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.0.0", + "karma-jasmine-html-reporter": "~1.7.0", + "ts-node": "~8.3.0", + "typescript": "~4.7.2" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", + "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==", + "dev": true + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1402.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.5.tgz", + "integrity": "sha512-vtJEwB51UEY1Q7FCI7xGLdhdb2SRTtI1Qs0or95momn85NuxlaMQsXK1Wxu9/EwtWKZK8dXePXbB/hpiNt61JQ==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.5", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.5.tgz", + "integrity": "sha512-jSgH11E+zs1C24lXj7R/PgXsTUpoYoMr1GtO6mpVROgXL5czVlL+b/B1p2HwbcAKuI9WXb48X6OZ6fOZhDQlSg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1402.5", + "@angular-devkit/build-webpack": "0.1402.5", + "@angular-devkit/core": "14.2.5", + "@babel/core": "7.18.10", + "@babel/generator": "7.18.12", + "@babel/helper-annotate-as-pure": "7.18.6", + "@babel/plugin-proposal-async-generator-functions": "7.18.10", + "@babel/plugin-transform-async-to-generator": "7.18.6", + "@babel/plugin-transform-runtime": "7.18.10", + "@babel/preset-env": "7.18.10", + "@babel/runtime": "7.18.9", + "@babel/template": "7.18.10", + "@discoveryjs/json-ext": "0.5.7", + "@ngtools/webpack": "14.2.5", + "ansi-colors": "4.1.3", + "babel-loader": "8.2.5", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "16.1.2", + "copy-webpack-plugin": "11.0.0", + "critters": "0.0.16", + "css-loader": "6.7.1", + "esbuild-wasm": "0.15.5", + "glob": "8.0.3", + "https-proxy-agent": "5.0.1", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.3", + "less-loader": "11.0.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.2.0", + "mini-css-extract-plugin": "2.6.1", + "minimatch": "5.1.0", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.16", + "postcss-import": "15.0.0", + "postcss-loader": "7.0.1", + "postcss-preset-env": "7.8.0", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.54.4", + "sass-loader": "13.0.2", + "semver": "7.3.7", + "source-map-loader": "4.0.0", + "source-map-support": "0.5.21", + "stylus": "0.59.0", + "stylus-loader": "7.0.0", + "terser": "5.14.2", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.4.0", + "webpack": "5.74.0", + "webpack-dev-middleware": "5.3.3", + "webpack-dev-server": "4.11.0", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.15.5" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0", + "@angular/localize": "^14.0.0", + "@angular/service-worker": "^14.0.0", + "karma": "^6.3.0", + "ng-packagr": "^14.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=4.6.2 <4.9" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1402.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.5.tgz", + "integrity": "sha512-h+o0GZD9iATwWjaTiUR0lJ3QZ9twUOJ1sotRchXHzAXMuaDk8wqqPriL5S0qDMlA2QqpNt4OD9rodUCRwae7fw==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1402.5", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^4.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/core": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.5.tgz", + "integrity": "sha512-lSje+HX0fx9Y2A4k63jVHrWdGT4wellhwcZpTCv9P6LvdfTkAlrfra3TaYhUPjavCsPwlRC/VVQN3Qkzk5m6gA==", + "dev": true, + "dependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/schematics": { + "version": "14.0.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.0.7.tgz", + "integrity": "sha512-nJUJXCBQr7rmVn6IXFAXMCWAB1w6JQmFGuFVW0G3GH/A0e+A3ttzJc6qVLYluqaFoafw394cZu24YJo55E/+Zg==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.0.7", + "jsonc-parser": "3.0.0", + "magic-string": "0.26.1", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/@angular-devkit/core": { + "version": "14.0.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.0.7.tgz", + "integrity": "sha512-XBqK2OMVKkV1Ltkh4aBsoHoDJQlins5a6qa/ZMjW4reYx90qLERs8ZfeWlRUWhvn2/ohx4aPq77jwSR5avp/Cw==", + "dev": true, + "dependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.0.0", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics/node_modules/jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular/animations": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.2.5.tgz", + "integrity": "sha512-4BhR9jSjgIwoK/alu7FSwSU5SxISMVFBAl/4cEYchfCqnflMNkZ8WwRVKTQjyeuYW5KtQTw9jRNp4tGK1YQWYw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "14.2.5" + } + }, + "node_modules/@angular/cli": { + "version": "14.0.7", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.0.7.tgz", + "integrity": "sha512-tABt1EDwBHm0ngsutdkXXWgPgHzapGLC7rSPHXStMc24ngViFZpXGzBCpompjHvXNt6bjklmJmuRvjS6+ktBZA==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1400.7", + "@angular-devkit/core": "14.0.7", + "@angular-devkit/schematics": "14.0.7", + "@schematics/angular": "14.0.7", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.1", + "debug": "4.3.4", + "ini": "3.0.0", + "inquirer": "8.2.4", + "jsonc-parser": "3.0.0", + "npm-package-arg": "9.0.2", + "npm-pick-manifest": "7.0.1", + "open": "8.4.0", + "ora": "5.4.1", + "pacote": "13.3.0", + "resolve": "1.22.0", + "semver": "7.3.7", + "symbol-observable": "4.0.0", + "uuid": "8.3.2", + "yargs": "17.4.1" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/cli/node_modules/@angular-devkit/architect": { + "version": "0.1400.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1400.7.tgz", + "integrity": "sha512-8dv/Ql86dHajsHYjjr5jvpiV7uXWbt7Mz4K/rGiOi+zzDNKPcZcuCejulWhOySDcCPjT/an47Qcwr+awL4Wr4g==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.0.7", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/cli/node_modules/@angular-devkit/core": { + "version": "14.0.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.0.7.tgz", + "integrity": "sha512-XBqK2OMVKkV1Ltkh4aBsoHoDJQlins5a6qa/ZMjW4reYx90qLERs8ZfeWlRUWhvn2/ohx4aPq77jwSR5avp/Cw==", + "dev": true, + "dependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.0.0", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular/cli/node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@angular/cli/node_modules/jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "node_modules/@angular/cli/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular/cli/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@angular/cli/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular/common": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.2.5.tgz", + "integrity": "sha512-v2fIK6imfMkUvYNjZQO+drE39QO3eSS95Yy7UN+6inb47DkAfzx6hipA9zKrMENjsS3kDv1d7cgDHE7WuOCzIw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "14.2.5", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.2.5.tgz", + "integrity": "sha512-L7d2/D6o9wlB2ugqRYpev6a8JntqS+7lF2o6z8y7RR2YAlAu71nq0BDsQez4/aSCK3HnDq0yhEnns7vcmOq/jA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "14.2.5" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + } + } + }, + "node_modules/@angular/compiler-cli": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.2.5.tgz", + "integrity": "sha512-3GYzTPw96TfJjw7Aso+f+uN6VFBWedqRATUQ6v+BAEyZIboirdLI1JQFOcCfuKWUM2B48RW+pdIduZmG3ckotA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.17.2", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.26.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js", + "ngcc": "bundles/ngcc/main-ngcc.js" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/compiler": "14.2.5", + "typescript": ">=4.6.2 <4.9" + } + }, + "node_modules/@angular/core": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.2.5.tgz", + "integrity": "sha512-Ok78Abq0puMGlolvNVzKFvsX7ePDkyxpZzztDzXDdRA4x4o6bAuuDG9Y7Wab2+wsdY6NktO+dFQjq1UBWClgSg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.11.4" + } + }, + "node_modules/@angular/forms": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.2.5.tgz", + "integrity": "sha512-aMH5Vrftny0KF0XzWQIGfHoI0LVQ2aatpWzdUWiUqBeX/Q+ucmxeP5rZyKtUsi0flETWxdRZSBTjbXZ3dsIcTA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "14.2.5", + "@angular/core": "14.2.5", + "@angular/platform-browser": "14.2.5", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.2.5.tgz", + "integrity": "sha512-FDZm23N9veSEouQX1YuZUjv7Nillroi+v0VbN1x5iPpFZEudaoZYT3A7bpJwdlxUx/4rGS0caaXNhN3CowtIeQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/animations": "14.2.5", + "@angular/common": "14.2.5", + "@angular/core": "14.2.5" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.2.5.tgz", + "integrity": "sha512-7W8oLs8YEGRr8izgUlpHgBfg3vUb5H0yicTHJY4zIqHJJbG1rTl46CjULaMjYM/FWcS8o7y6XJJcHx0c7pKNsw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "14.2.5", + "@angular/compiler": "14.2.5", + "@angular/core": "14.2.5", + "@angular/platform-browser": "14.2.5" + } + }, + "node_modules/@angular/router": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.2.5.tgz", + "integrity": "sha512-AUHcr9Lln7emJ/aete08UoqWQFZOLH1MhuP78r2pixvnNiZ9C8hcevX1rGGax0Po/Gy4PSJ4wnFhZPgifqCguQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "14.2.5", + "@angular/core": "14.2.5", + "@angular/platform-browser": "14.2.5", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", + "dev": true + }, + "node_modules/@auth0/angular-jwt": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@auth0/angular-jwt/-/angular-jwt-5.1.0.tgz", + "integrity": "sha512-EAQoNKPQSZYphcX6FnY2e7xQpD4ZdHQ1DbHq/m+G1U1qA60m3XnhdjPPfu+blVHARlxEbRzWXc48UOVrnMsrZw==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": ">=12.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz", + "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.18.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", + "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "dev": true, + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", + "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.19.3", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", + "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", + "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", + "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", + "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", + "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.19.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", + "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", + "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.4", + "@babel/types": "^7.19.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.4.tgz", + "integrity": "sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", + "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.19.4.tgz", + "integrity": "sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.19.4", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.18.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", + "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.19.4.tgz", + "integrity": "sha512-934S2VLLlt2hRJwPf4MczaOr4hYF0z+VKPwqTNxyKX7NthTiPfhuKFWQZHXRM0vh/wo/VyXB3s4bZUNA08l+tQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz", + "integrity": "sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.19.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.19.4.tgz", + "integrity": "sha512-t0j0Hgidqf0aM86dF8U+vXYReUgJnlv4bZLsyoPnwZNrGY+7/38o8YjaELrvHeVfTZao15kjR0PVv0nju2iduA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", + "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", + "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz", + "integrity": "sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", + "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", + "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", + "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", + "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.18.9", + "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.18.10", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "core-js-compat": "^3.22.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.4.tgz", + "integrity": "sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.4", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.19.4", + "@babel/types": "^7.19.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.4.tgz", + "integrity": "sha512-5T2lY5vXqS+5UEit/5TwcIUeCnwgCljcF8IQRT6XRQPBrvLeq5V8W+URv+GvwoF3FP8tkhp++evVyDzkDGzNmA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.19.4", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz", + "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@csstools/postcss-cascade-layers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", + "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2", + "postcss-selector-parser": "^6.0.10" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.5.tgz", + "integrity": "sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.16", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz", + "integrity": "sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "dev": true + }, + "node_modules/@microsoft/signalr": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-6.0.9.tgz", + "integrity": "sha512-DGVYe3ycT2PfRU7m3xCbv1HjhvClKl2VB1HyFlvf8SqBGXz3Cx+oalNWGYrGIgADA6Q2xaB4GaDmDdprTa2U0Q==", + "dependencies": { + "abort-controller": "^3.0.0", + "eventsource": "^1.0.7", + "fetch-cookie": "^0.11.0", + "node-fetch": "^2.6.7", + "ws": "^7.4.5" + } + }, + "node_modules/@microsoft/signalr/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@ngtools/webpack": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.5.tgz", + "integrity": "sha512-Thwq1WyOOq1PIWMcjAAqKI1hbvGC0ywxbNoDadOlWpEFm6k0dvXC6Zm9lnVkePjxlPfagvbnv55+Lv9Vmygc1g==", + "dev": true, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0", + "typescript": ">=4.6.2 <4.9", + "webpack": "^5.54.0" + } + }, + "node_modules/@ngx-translate/core": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-14.0.0.tgz", + "integrity": "sha512-UevdwNCXMRCdJv//0kC8h2eSfmi02r29xeE8E9gJ1Al4D4jEJ7eiLPdjslTMc21oJNGguqqWeEVjf64SFtvw2w==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/core": ">=13.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@ngx-translate/http-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-7.0.0.tgz", + "integrity": "sha512-j+NpXXlcGVdyUNyY/qsJrqqeAdJdizCd+GKh3usXExSqy1aE9866jlAIL+xrfDU4w+LiMoma5pgE4emvFebZmA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=13.0.0", + "@ngx-translate/core": ">=14.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-3.0.2.tgz", + "integrity": "sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^3.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^7.0.0", + "proc-log": "^2.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "dev": true, + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz", + "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz", + "integrity": "sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g==", + "dev": true, + "dependencies": { + "infer-owner": "^1.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-3.0.3.tgz", + "integrity": "sha512-ZXL6qgC5NjwfZJ2nET+ZSLEz/PJgJ/5CU90C2S66dZY4Jw73DasS4ZCXuy/KHWYP0imjJ4VtA+Gebb5BxxKp9Q==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/promise-spawn": "^3.0.0", + "node-gyp": "^8.4.1", + "read-package-json-fast": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@schematics/angular": { + "version": "14.0.7", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-14.0.7.tgz", + "integrity": "sha512-I0v1gNFpm9ReL/hUzwjjOa+hk0qvlXv/vjITAWnlUV5dba6FZxzwsrTGsGO6t5XMNsm6QtwpDYDRdy9uy/n/1g==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.0.7", + "@angular-devkit/schematics": "14.0.7", + "jsonc-parser": "3.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@schematics/angular/node_modules/@angular-devkit/core": { + "version": "14.0.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.0.7.tgz", + "integrity": "sha512-XBqK2OMVKkV1Ltkh4aBsoHoDJQlins5a6qa/ZMjW4reYx90qLERs8ZfeWlRUWhvn2/ohx4aPq77jwSR5avp/Cw==", + "dev": true, + "dependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.0.0", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@schematics/angular/node_modules/jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "node_modules/@schematics/angular/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@schematics/angular/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@schematics/angular/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", + "dev": true + }, + "node_modules/@types/eslint": { + "version": "8.4.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", + "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.31", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", + "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jasmine": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.0.3.tgz", + "integrity": "sha512-Opp1LvvEuZdk8fSSvchK2mZwhVrsNT0JgJE9Di6MjnaIpmEXM8TLCPPrVtNTYh8+5MPdY8j9bAHMu2SSfwpZJg==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.8.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz", + "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/agentkeepalive/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "node_modules/autoprefixer": { + "version": "10.4.12", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.12.tgz", + "integrity": "sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001407", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.2", + "core-js-compat": "^3.21.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/bonjour-service": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", + "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", + "dev": true, + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.2.tgz", + "integrity": "sha512-Xx+xPlfCZIUHagysjjOAje9nRo8pRDczQCcXb4J2O0BLtH+xeVue6ba4y1kfJfQMAnM2mkcoMIAyOctlaRGWYA==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001418", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz", + "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/core-js-compat": { + "version": "3.25.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.25.5.tgz", + "integrity": "sha512-ovcyhs2DEBUIE0MGEKHP4olCUW/XYte3Vroyxuh38rD1wAO4dHohsovUC4eAOuzFxE6b+RXvBU3UZ9o0YhUTkA==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/critters": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^4.2.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + } + }, + "node_modules/critters/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/critters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/critters/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/critters/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/critters/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/critters/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "dev": true, + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssdb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.0.1.tgz", + "integrity": "sha512-pT3nzyGM78poCKLAEy2zWIVX2hikq6dIrjuZzLV98MumBg+xMTNYfHx7paUlfiRTgg91O/vR889CIf+qiv79Rw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true + }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "dev": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true + }, + "node_modules/dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "dev": true, + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "dev": true, + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.276", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.276.tgz", + "integrity": "sha512-EpuHPqu8YhonqLBXHoU6hDJCD98FCe6KDoet3/gY1qsQ6usjJoHqBH2YIVs8FXaAtHwVL8Uqa/fsYao/vq9VWQ==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/engine.io": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", + "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", + "dev": true, + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", + "dev": true + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz", + "integrity": "sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/linux-loong64": "0.15.5", + "esbuild-android-64": "0.15.5", + "esbuild-android-arm64": "0.15.5", + "esbuild-darwin-64": "0.15.5", + "esbuild-darwin-arm64": "0.15.5", + "esbuild-freebsd-64": "0.15.5", + "esbuild-freebsd-arm64": "0.15.5", + "esbuild-linux-32": "0.15.5", + "esbuild-linux-64": "0.15.5", + "esbuild-linux-arm": "0.15.5", + "esbuild-linux-arm64": "0.15.5", + "esbuild-linux-mips64le": "0.15.5", + "esbuild-linux-ppc64le": "0.15.5", + "esbuild-linux-riscv64": "0.15.5", + "esbuild-linux-s390x": "0.15.5", + "esbuild-netbsd-64": "0.15.5", + "esbuild-openbsd-64": "0.15.5", + "esbuild-sunos-64": "0.15.5", + "esbuild-windows-32": "0.15.5", + "esbuild-windows-64": "0.15.5", + "esbuild-windows-arm64": "0.15.5" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.5.tgz", + "integrity": "sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.5.tgz", + "integrity": "sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.5.tgz", + "integrity": "sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.5.tgz", + "integrity": "sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.5.tgz", + "integrity": "sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.5.tgz", + "integrity": "sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.5.tgz", + "integrity": "sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.5.tgz", + "integrity": "sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.5.tgz", + "integrity": "sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.5.tgz", + "integrity": "sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.5.tgz", + "integrity": "sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.5.tgz", + "integrity": "sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.5.tgz", + "integrity": "sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.5.tgz", + "integrity": "sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.5.tgz", + "integrity": "sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.5.tgz", + "integrity": "sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.5.tgz", + "integrity": "sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.5.tgz", + "integrity": "sha512-lTJOEKekN/4JI/eOEq0wLcx53co2N6vaT/XjBz46D1tvIVoUEyM0o2K6txW6gEotf31szFD/J1PbxmnbkGlK9A==", + "dev": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.5.tgz", + "integrity": "sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.5.tgz", + "integrity": "sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.5.tgz", + "integrity": "sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter-asyncresource": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.2.tgz", + "integrity": "sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/express/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fetch-cookie": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz", + "integrity": "sha512-BQm7iZLFhMWFy5CZ/162sAGjBfdNWb7a8LEqqnzsHFhxT/X/SVj/z2t2nu3aJvjlbQkrAlTUApplPRjWyH4mhA==", + "dependencies": { + "tough-cookie": "^2.3.3 || ^3.0.1 || ^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/finalhandler/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, + "node_modules/hdr-histogram-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", + "dev": true, + "dependencies": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "node_modules/hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", + "dev": true + }, + "node_modules/hosted-git-info": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.1.0.tgz", + "integrity": "sha512-Ek+QmMEqZF8XrbFdwoDjSbm7rT23pCgEMOJmz6GPk/s4yH//RQfNPArhIxbguNxROq/+5lNBwCDHMhA903Kx1Q==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz", + "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/inquirer": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jasmine-core": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.1.1.tgz", + "integrity": "sha512-lmUfT5XcK9KKvt3lLYzn93hc4MGzlUBowExFVgzbSW0ZCrdeyS574dfsyfRhxbg81Wj4gk+RxUiTnj7KBfDA1g==", + "dev": true + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/karma": { + "version": "6.3.20", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.20.tgz", + "integrity": "sha512-HRNQhMuKOwKpjYlWiJP0DUrJOh+QjaI/DTaD8b9rEm4Il3tJ8MijutVZH4ts10LuUFst/CedwTS6vieCN8yTSw==", + "dev": true, + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.4.1", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-chrome-launcher": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz", + "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==", + "dev": true, + "dependencies": { + "which": "^1.2.1" + } + }, + "node_modules/karma-coverage": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.0.tgz", + "integrity": "sha512-gPVdoZBNDZ08UCzdMHHhEImKrw1+PAOQOIiffv1YsvxFhBjqvo/SVXNk4tqn1SYqX0BJZT6S/59zgxiBe+9OuA==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.0.5", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/karma-coverage/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/karma-coverage/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/karma-jasmine": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.0.1.tgz", + "integrity": "sha512-FkL1Kk+JAKmim8VWU8RXKZBpl0lLI7J8LijM0/q7oP7emfB6QMZV1Az+JgqGKSLpF0tYaav+KUVFQroZUxQTHA==", + "dev": true, + "dependencies": { + "jasmine-core": "^4.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "karma": "^6.0.0" + } + }, + "node_modules/karma-jasmine-html-reporter": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz", + "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==", + "dev": true, + "peerDependencies": { + "jasmine-core": ">=3.8", + "karma": ">=0.9", + "karma-jasmine": ">=1.1" + } + }, + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "dependencies": { + "source-map-support": "^0.5.5" + } + }, + "node_modules/karma/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/karma/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/karma/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/karma/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/karma/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/karma/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz", + "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==", + "dev": true, + "dependencies": { + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/less/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", + "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log4js": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.7.0.tgz", + "integrity": "sha512-KA0W9ffgNBLDj6fZCq/lRbgR6ABAodRIDHrZnS48vOtfKa4PzWImb0Md1lmGCdO3n3sbCm/n1/WmrNlZ8kCI3Q==", + "dev": true, + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.3" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/lru-cache": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.0.tgz", + "integrity": "sha512-EIRtP1GrSJny0dqb50QXRUNBxHJhcpxHC++M5tD7RYbvLLn5KVWKsbyswSSqDuU15UFi3bgTQIY8nhDMeF6aDQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/magic-string": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz", + "integrity": "sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/make-fetch-happen/node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/make-fetch-happen/node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/make-fetch-happen/node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", + "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", + "dev": true, + "dependencies": { + "fs-monkey": "^1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "node_modules/minipass": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", + "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-json-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", + "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", + "dev": true, + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/needle": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.1.0.tgz", + "integrity": "sha512-gCE9weDhjVGCRqS8dwDR/D3GTAeyXLXuqp7I8EzH6DllZGXSUyxuqqLh+YX9rMAWaaTFyVAg6rHGL25dqvczKw==", + "dev": true, + "optional": true, + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "optional": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "!win32" + ], + "dependencies": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", + "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==", + "dev": true, + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-package-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", + "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-install-checks": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", + "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "node_modules/npm-package-arg": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.0.2.tgz", + "integrity": "sha512-v/miORuX8cndiOheW8p2moNuPJ7QhcFh9WGlTorruG8hXSA23vMTEp5hTCmDxic0nD8KHhj/NQgFuySD3GYY3g==", + "dev": true, + "dependencies": { + "hosted-git-info": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", + "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", + "dev": true, + "dependencies": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist/node_modules/npm-bundled": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", + "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz", + "integrity": "sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg==", + "dev": true, + "dependencies": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz", + "integrity": "sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^10.0.6", + "minipass": "^3.1.6", + "minipass-fetch": "^2.0.3", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^9.0.1", + "proc-log": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm-registry-fetch/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pacote": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.3.0.tgz", + "integrity": "sha512-auhJAUlfC2TALo6I0s1vFoPvVFgWGx+uz/PnIojTTgkGwlK3Np8sGJ0ghfFhiuzJXTZoTycMLk8uLskdntPbDw==", + "dev": true, + "dependencies": { + "@npmcli/git": "^3.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/promise-spawn": "^3.0.0", + "@npmcli/run-script": "^3.0.1", + "cacache": "^16.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.6", + "mkdirp": "^1.0.4", + "npm-package-arg": "^9.0.0", + "npm-packlist": "^5.0.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^5.0.0", + "read-package-json-fast": "^2.0.3", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/piscina": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", + "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", + "dev": true, + "dependencies": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0" + }, + "optionalDependencies": { + "nice-napi": "^1.0.2" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss": { + "version": "8.4.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", + "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=7.6.0" + }, + "peerDependencies": { + "postcss": "^8.4.6" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-custom-properties": { + "version": "12.1.9", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.9.tgz", + "integrity": "sha512-/E7PRvK8DAVljBbeWrcEQJPG72jaImxF3vvCNFwv9cC8CzigVoNIpeyfnJzphnN3Fd8/auBf5wvkw6W9MfmTyg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "dev": true, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-import": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.0.0.tgz", + "integrity": "sha512-Y20shPQ07RitgBGv2zvkEAu9bqvrD77C9axhj/aA1BQj4czape2MdClCExvB27EwYEJdGgKZBpKanb0t1rK2Kg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "dev": true, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-loader": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", + "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.7" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nesting": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-opacity-percentage": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", + "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==", + "dev": true, + "funding": [ + { + "type": "kofi", + "url": "https://ko-fi.com/mrcgrtz" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mrcgrtz" + } + ], + "engines": { + "node": "^12 || ^14 || >=16" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "dev": true, + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-preset-env": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz", + "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==", + "dev": true, + "dependencies": { + "@csstools/postcss-cascade-layers": "^1.0.5", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.8", + "browserslist": "^4.21.3", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.0.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.8", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.10", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "dev": true, + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/primeicons": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-6.0.1.tgz", + "integrity": "sha512-KDeO94CbWI4pKsPnYpA1FPjo79EsY9I+M8ywoPBSf9XMXoe/0crjbUK7jcQEDHuc0ZMRIZsxH3TYLv4TUtHmAA==" + }, + "node_modules/primeng": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/primeng/-/primeng-14.1.2.tgz", + "integrity": "sha512-iLMeORiLD46SNAotxCwRXoaRMXLs3ZbFzyePrPSNAFQbKEbsLfpUvsAUAatb/TA0jd8TnXgdCjZ07ee4664XVQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^14.0.0", + "@angular/core": "^14.0.0", + "@angular/forms": "^14.0.0", + "primeicons": "^6.0.1", + "rxjs": "^6.0.0 || ^7.0.0", + "zone.js": "^0.10.2 || ^0.11.0" + } + }, + "node_modules/proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true, + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-package-json": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.2.tgz", + "integrity": "sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q==", + "dev": true, + "dependencies": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^2.3.1", + "normalize-package-data": "^4.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/read-package-json/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "node_modules/regexpu-core": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.1.tgz", + "integrity": "sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "devOptional": true + }, + "node_modules/sass": { + "version": "1.54.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz", + "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/sass-loader": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", + "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "dev": true, + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.2.tgz", + "integrity": "sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.2.0", + "socket.io-adapter": "~2.4.0", + "socket.io-parser": "~4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", + "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", + "dev": true + }, + "node_modules/socket.io-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", + "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "dev": true, + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz", + "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/streamroller": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.3.tgz", + "integrity": "sha512-CphIJyFx2SALGHeINanjFRKQ4l7x2c+rXYJ4BMq0gd+ZK0gi4VT8b+eHe2wi58x4UayBAKx4xtHpXT/ea1cz8w==", + "dev": true, + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/stylus": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", + "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://opencollective.com/stylus" + } + }, + "node_modules/stylus-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.0.0.tgz", + "integrity": "sha512-WTbtLrNfOfLgzTaR9Lj/BPhQroKk/LC1hfTXSUbrxmxgfUo3Y3LpmKRVA2R1XbjvTAvOfaian9vOyfv1z99E+A==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "klona": "^2.0.5", + "normalize-path": "^3.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "stylus": ">=0.52.4", + "webpack": "^5.0.0" + } + }, + "node_modules/stylus/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/stylus/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/stylus/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.14", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.14.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-node": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", + "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", + "dev": true, + "dependencies": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + }, + "bin": { + "ts-node": "dist/bin.js" + }, + "engines": { + "node": ">=4.2.0" + }, + "peerDependencies": { + "typescript": ">=2.0" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "node_modules/typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", + "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", + "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/webpack": { + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz", + "integrity": "sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.9.0.tgz", + "integrity": "sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "dependencies": { + "typed-assert": "^1.0.8" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", + "webpack": "^5.12.0" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", + "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/zone.js": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz", + "integrity": "sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==", + "dependencies": { + "tslib": "^2.3.0" + } + } + }, + "dependencies": { + "@adobe/css-tools": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", + "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==", + "dev": true + }, + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@angular-devkit/architect": { + "version": "0.1402.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.5.tgz", + "integrity": "sha512-vtJEwB51UEY1Q7FCI7xGLdhdb2SRTtI1Qs0or95momn85NuxlaMQsXK1Wxu9/EwtWKZK8dXePXbB/hpiNt61JQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "14.2.5", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/build-angular": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.5.tgz", + "integrity": "sha512-jSgH11E+zs1C24lXj7R/PgXsTUpoYoMr1GtO6mpVROgXL5czVlL+b/B1p2HwbcAKuI9WXb48X6OZ6fOZhDQlSg==", + "dev": true, + "requires": { + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1402.5", + "@angular-devkit/build-webpack": "0.1402.5", + "@angular-devkit/core": "14.2.5", + "@babel/core": "7.18.10", + "@babel/generator": "7.18.12", + "@babel/helper-annotate-as-pure": "7.18.6", + "@babel/plugin-proposal-async-generator-functions": "7.18.10", + "@babel/plugin-transform-async-to-generator": "7.18.6", + "@babel/plugin-transform-runtime": "7.18.10", + "@babel/preset-env": "7.18.10", + "@babel/runtime": "7.18.9", + "@babel/template": "7.18.10", + "@discoveryjs/json-ext": "0.5.7", + "@ngtools/webpack": "14.2.5", + "ansi-colors": "4.1.3", + "babel-loader": "8.2.5", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "16.1.2", + "copy-webpack-plugin": "11.0.0", + "critters": "0.0.16", + "css-loader": "6.7.1", + "esbuild": "0.15.5", + "esbuild-wasm": "0.15.5", + "glob": "8.0.3", + "https-proxy-agent": "5.0.1", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.3", + "less-loader": "11.0.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.2.0", + "mini-css-extract-plugin": "2.6.1", + "minimatch": "5.1.0", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.16", + "postcss-import": "15.0.0", + "postcss-loader": "7.0.1", + "postcss-preset-env": "7.8.0", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.54.4", + "sass-loader": "13.0.2", + "semver": "7.3.7", + "source-map-loader": "4.0.0", + "source-map-support": "0.5.21", + "stylus": "0.59.0", + "stylus-loader": "7.0.0", + "terser": "5.14.2", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.4.0", + "webpack": "5.74.0", + "webpack-dev-middleware": "5.3.3", + "webpack-dev-server": "4.11.0", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + } + } + }, + "@angular-devkit/build-webpack": { + "version": "0.1402.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.5.tgz", + "integrity": "sha512-h+o0GZD9iATwWjaTiUR0lJ3QZ9twUOJ1sotRchXHzAXMuaDk8wqqPriL5S0qDMlA2QqpNt4OD9rodUCRwae7fw==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1402.5", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/core": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.5.tgz", + "integrity": "sha512-lSje+HX0fx9Y2A4k63jVHrWdGT4wellhwcZpTCv9P6LvdfTkAlrfra3TaYhUPjavCsPwlRC/VVQN3Qkzk5m6gA==", + "dev": true, + "requires": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/schematics": { + "version": "14.0.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.0.7.tgz", + "integrity": "sha512-nJUJXCBQr7rmVn6IXFAXMCWAB1w6JQmFGuFVW0G3GH/A0e+A3ttzJc6qVLYluqaFoafw394cZu24YJo55E/+Zg==", + "dev": true, + "requires": { + "@angular-devkit/core": "14.0.7", + "jsonc-parser": "3.0.0", + "magic-string": "0.26.1", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "14.0.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.0.7.tgz", + "integrity": "sha512-XBqK2OMVKkV1Ltkh4aBsoHoDJQlins5a6qa/ZMjW4reYx90qLERs8ZfeWlRUWhvn2/ohx4aPq77jwSR5avp/Cw==", + "dev": true, + "requires": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.0.0", + "rxjs": "6.6.7", + "source-map": "0.7.3" + } + }, + "jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular/animations": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.2.5.tgz", + "integrity": "sha512-4BhR9jSjgIwoK/alu7FSwSU5SxISMVFBAl/4cEYchfCqnflMNkZ8WwRVKTQjyeuYW5KtQTw9jRNp4tGK1YQWYw==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/cli": { + "version": "14.0.7", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.0.7.tgz", + "integrity": "sha512-tABt1EDwBHm0ngsutdkXXWgPgHzapGLC7rSPHXStMc24ngViFZpXGzBCpompjHvXNt6bjklmJmuRvjS6+ktBZA==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1400.7", + "@angular-devkit/core": "14.0.7", + "@angular-devkit/schematics": "14.0.7", + "@schematics/angular": "14.0.7", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.1", + "debug": "4.3.4", + "ini": "3.0.0", + "inquirer": "8.2.4", + "jsonc-parser": "3.0.0", + "npm-package-arg": "9.0.2", + "npm-pick-manifest": "7.0.1", + "open": "8.4.0", + "ora": "5.4.1", + "pacote": "13.3.0", + "resolve": "1.22.0", + "semver": "7.3.7", + "symbol-observable": "4.0.0", + "uuid": "8.3.2", + "yargs": "17.4.1" + }, + "dependencies": { + "@angular-devkit/architect": { + "version": "0.1400.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1400.7.tgz", + "integrity": "sha512-8dv/Ql86dHajsHYjjr5jvpiV7uXWbt7Mz4K/rGiOi+zzDNKPcZcuCejulWhOySDcCPjT/an47Qcwr+awL4Wr4g==", + "dev": true, + "requires": { + "@angular-devkit/core": "14.0.7", + "rxjs": "6.6.7" + } + }, + "@angular-devkit/core": { + "version": "14.0.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.0.7.tgz", + "integrity": "sha512-XBqK2OMVKkV1Ltkh4aBsoHoDJQlins5a6qa/ZMjW4reYx90qLERs8ZfeWlRUWhvn2/ohx4aPq77jwSR5avp/Cw==", + "dev": true, + "requires": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.0.0", + "rxjs": "6.6.7", + "source-map": "0.7.3" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular/common": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.2.5.tgz", + "integrity": "sha512-v2fIK6imfMkUvYNjZQO+drE39QO3eSS95Yy7UN+6inb47DkAfzx6hipA9zKrMENjsS3kDv1d7cgDHE7WuOCzIw==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/compiler": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.2.5.tgz", + "integrity": "sha512-L7d2/D6o9wlB2ugqRYpev6a8JntqS+7lF2o6z8y7RR2YAlAu71nq0BDsQez4/aSCK3HnDq0yhEnns7vcmOq/jA==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/compiler-cli": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.2.5.tgz", + "integrity": "sha512-3GYzTPw96TfJjw7Aso+f+uN6VFBWedqRATUQ6v+BAEyZIboirdLI1JQFOcCfuKWUM2B48RW+pdIduZmG3ckotA==", + "dev": true, + "requires": { + "@babel/core": "^7.17.2", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.26.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + } + }, + "@angular/core": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.2.5.tgz", + "integrity": "sha512-Ok78Abq0puMGlolvNVzKFvsX7ePDkyxpZzztDzXDdRA4x4o6bAuuDG9Y7Wab2+wsdY6NktO+dFQjq1UBWClgSg==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/forms": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.2.5.tgz", + "integrity": "sha512-aMH5Vrftny0KF0XzWQIGfHoI0LVQ2aatpWzdUWiUqBeX/Q+ucmxeP5rZyKtUsi0flETWxdRZSBTjbXZ3dsIcTA==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/platform-browser": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.2.5.tgz", + "integrity": "sha512-FDZm23N9veSEouQX1YuZUjv7Nillroi+v0VbN1x5iPpFZEudaoZYT3A7bpJwdlxUx/4rGS0caaXNhN3CowtIeQ==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.2.5.tgz", + "integrity": "sha512-7W8oLs8YEGRr8izgUlpHgBfg3vUb5H0yicTHJY4zIqHJJbG1rTl46CjULaMjYM/FWcS8o7y6XJJcHx0c7pKNsw==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/router": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.2.5.tgz", + "integrity": "sha512-AUHcr9Lln7emJ/aete08UoqWQFZOLH1MhuP78r2pixvnNiZ9C8hcevX1rGGax0Po/Gy4PSJ4wnFhZPgifqCguQ==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", + "dev": true + }, + "@auth0/angular-jwt": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@auth0/angular-jwt/-/angular-jwt-5.1.0.tgz", + "integrity": "sha512-EAQoNKPQSZYphcX6FnY2e7xQpD4ZdHQ1DbHq/m+G1U1qA60m3XnhdjPPfu+blVHARlxEbRzWXc48UOVrnMsrZw==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz", + "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==", + "dev": true + }, + "@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.18.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", + "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "dev": true, + "requires": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", + "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.19.3", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", + "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", + "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "dev": true, + "requires": { + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", + "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-replace-supers": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", + "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-simple-access": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", + "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", + "dev": true, + "requires": { + "@babel/types": "^7.19.4" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "dev": true, + "requires": { + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", + "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + } + }, + "@babel/helpers": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", + "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.4", + "@babel/types": "^7.19.4" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.4.tgz", + "integrity": "sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==", + "dev": true + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", + "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.19.4.tgz", + "integrity": "sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.19.4", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.18.8" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", + "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.19.4.tgz", + "integrity": "sha512-934S2VLLlt2hRJwPf4MczaOr4hYF0z+VKPwqTNxyKX7NthTiPfhuKFWQZHXRM0vh/wo/VyXB3s4bZUNA08l+tQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz", + "integrity": "sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.19.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.19.4.tgz", + "integrity": "sha512-t0j0Hgidqf0aM86dF8U+vXYReUgJnlv4bZLsyoPnwZNrGY+7/38o8YjaELrvHeVfTZao15kjR0PVv0nju2iduA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", + "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", + "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz", + "integrity": "sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", + "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", + "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", + "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/preset-env": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", + "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.18.9", + "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.18.10", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "core-js-compat": "^3.22.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.4.tgz", + "integrity": "sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.4", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.19.4", + "@babel/types": "^7.19.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.4.tgz", + "integrity": "sha512-5T2lY5vXqS+5UEit/5TwcIUeCnwgCljcF8IQRT6XRQPBrvLeq5V8W+URv+GvwoF3FP8tkhp++evVyDzkDGzNmA==", + "dev": true, + "requires": { + "@babel/types": "^7.19.4", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/types": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz", + "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true + }, + "@csstools/postcss-cascade-layers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "dev": true, + "requires": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "dev": true, + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "dev": true, + "requires": {} + }, + "@csstools/selector-specificity": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", + "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "dev": true, + "requires": {} + }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@esbuild/linux-loong64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.5.tgz", + "integrity": "sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==", + "dev": true, + "optional": true + }, + "@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.16", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz", + "integrity": "sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "dev": true + }, + "@microsoft/signalr": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-6.0.9.tgz", + "integrity": "sha512-DGVYe3ycT2PfRU7m3xCbv1HjhvClKl2VB1HyFlvf8SqBGXz3Cx+oalNWGYrGIgADA6Q2xaB4GaDmDdprTa2U0Q==", + "requires": { + "abort-controller": "^3.0.0", + "eventsource": "^1.0.7", + "fetch-cookie": "^0.11.0", + "node-fetch": "^2.6.7", + "ws": "^7.4.5" + }, + "dependencies": { + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "requires": {} + } + } + }, + "@ngtools/webpack": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.5.tgz", + "integrity": "sha512-Thwq1WyOOq1PIWMcjAAqKI1hbvGC0ywxbNoDadOlWpEFm6k0dvXC6Zm9lnVkePjxlPfagvbnv55+Lv9Vmygc1g==", + "dev": true, + "requires": {} + }, + "@ngx-translate/core": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-14.0.0.tgz", + "integrity": "sha512-UevdwNCXMRCdJv//0kC8h2eSfmi02r29xeE8E9gJ1Al4D4jEJ7eiLPdjslTMc21oJNGguqqWeEVjf64SFtvw2w==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@ngx-translate/http-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-7.0.0.tgz", + "integrity": "sha512-j+NpXXlcGVdyUNyY/qsJrqqeAdJdizCd+GKh3usXExSqy1aE9866jlAIL+xrfDU4w+LiMoma5pgE4emvFebZmA==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "requires": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + } + }, + "@npmcli/git": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-3.0.2.tgz", + "integrity": "sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w==", + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^3.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^7.0.0", + "proc-log": "^2.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "@npmcli/installed-package-contents": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "dev": true, + "requires": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@npmcli/node-gyp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz", + "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==", + "dev": true + }, + "@npmcli/promise-spawn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz", + "integrity": "sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g==", + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-3.0.3.tgz", + "integrity": "sha512-ZXL6qgC5NjwfZJ2nET+ZSLEz/PJgJ/5CU90C2S66dZY4Jw73DasS4ZCXuy/KHWYP0imjJ4VtA+Gebb5BxxKp9Q==", + "dev": true, + "requires": { + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/promise-spawn": "^3.0.0", + "node-gyp": "^8.4.1", + "read-package-json-fast": "^2.0.3" + } + }, + "@schematics/angular": { + "version": "14.0.7", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-14.0.7.tgz", + "integrity": "sha512-I0v1gNFpm9ReL/hUzwjjOa+hk0qvlXv/vjITAWnlUV5dba6FZxzwsrTGsGO6t5XMNsm6QtwpDYDRdy9uy/n/1g==", + "dev": true, + "requires": { + "@angular-devkit/core": "14.0.7", + "@angular-devkit/schematics": "14.0.7", + "jsonc-parser": "3.0.0" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "14.0.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.0.7.tgz", + "integrity": "sha512-XBqK2OMVKkV1Ltkh4aBsoHoDJQlins5a6qa/ZMjW4reYx90qLERs8ZfeWlRUWhvn2/ohx4aPq77jwSR5avp/Cw==", + "dev": true, + "requires": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.0.0", + "rxjs": "6.6.7", + "source-map": "0.7.3" + } + }, + "jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", + "dev": true + }, + "@types/eslint": { + "version": "8.4.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", + "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "@types/express": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.31", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", + "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/jasmine": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.0.3.tgz", + "integrity": "sha512-Opp1LvvEuZdk8fSSvchK2mZwhVrsNT0JgJE9Di6MjnaIpmEXM8TLCPPrVtNTYh8+5MPdY8j9bAHMu2SSfwpZJg==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, + "@types/node": { + "version": "18.8.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz", + "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "requires": {} + }, + "adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true + } + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "autoprefixer": { + "version": "10.4.12", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.12.tgz", + "integrity": "sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q==", + "dev": true, + "requires": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001407", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.2", + "core-js-compat": "^3.21.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "bonjour-service": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", + "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", + "dev": true, + "requires": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "requires": { + "semver": "^7.0.0" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "cacache": { + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.2.tgz", + "integrity": "sha512-Xx+xPlfCZIUHagysjjOAje9nRo8pRDczQCcXb4J2O0BLtH+xeVue6ba4y1kfJfQMAnM2mkcoMIAyOctlaRGWYA==", + "dev": true, + "requires": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^1.1.1" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001418", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz", + "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "dev": true + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "requires": { + "is-what": "^3.14.1" + } + }, + "copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dev": true, + "requires": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "core-js-compat": { + "version": "3.25.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.25.5.tgz", + "integrity": "sha512-ovcyhs2DEBUIE0MGEKHP4olCUW/XYte3Vroyxuh38rD1wAO4dHohsovUC4eAOuzFxE6b+RXvBU3UZ9o0YhUTkA==", + "dev": true, + "requires": { + "browserslist": "^4.21.4" + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "critters": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "css-select": "^4.2.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.5" + } + }, + "css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "dev": true, + "requires": {} + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, + "cssdb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.0.1.tgz", + "integrity": "sha512-pT3nzyGM78poCKLAEy2zWIVX2hikq6dIrjuZzLV98MumBg+xMTNYfHx7paUlfiRTgg91O/vR889CIf+qiv79Rw==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true + }, + "date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + } + }, + "defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true + }, + "dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "dev": true, + "requires": { + "@leichtgewicht/ip-codec": "^2.0.1" + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "dev": true, + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.276", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.276.tgz", + "integrity": "sha512-EpuHPqu8YhonqLBXHoU6hDJCD98FCe6KDoet3/gY1qsQ6usjJoHqBH2YIVs8FXaAtHwVL8Uqa/fsYao/vq9VWQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "engine.io": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", + "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", + "dev": true, + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3" + } + }, + "engine.io-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "dev": true + }, + "enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", + "dev": true + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true + }, + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "esbuild": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz", + "integrity": "sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==", + "dev": true, + "optional": true, + "requires": { + "@esbuild/linux-loong64": "0.15.5", + "esbuild-android-64": "0.15.5", + "esbuild-android-arm64": "0.15.5", + "esbuild-darwin-64": "0.15.5", + "esbuild-darwin-arm64": "0.15.5", + "esbuild-freebsd-64": "0.15.5", + "esbuild-freebsd-arm64": "0.15.5", + "esbuild-linux-32": "0.15.5", + "esbuild-linux-64": "0.15.5", + "esbuild-linux-arm": "0.15.5", + "esbuild-linux-arm64": "0.15.5", + "esbuild-linux-mips64le": "0.15.5", + "esbuild-linux-ppc64le": "0.15.5", + "esbuild-linux-riscv64": "0.15.5", + "esbuild-linux-s390x": "0.15.5", + "esbuild-netbsd-64": "0.15.5", + "esbuild-openbsd-64": "0.15.5", + "esbuild-sunos-64": "0.15.5", + "esbuild-windows-32": "0.15.5", + "esbuild-windows-64": "0.15.5", + "esbuild-windows-arm64": "0.15.5" + } + }, + "esbuild-android-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.5.tgz", + "integrity": "sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==", + "dev": true, + "optional": true + }, + "esbuild-android-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.5.tgz", + "integrity": "sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.5.tgz", + "integrity": "sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.5.tgz", + "integrity": "sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.5.tgz", + "integrity": "sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.5.tgz", + "integrity": "sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.5.tgz", + "integrity": "sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.5.tgz", + "integrity": "sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.5.tgz", + "integrity": "sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.5.tgz", + "integrity": "sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.5.tgz", + "integrity": "sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.5.tgz", + "integrity": "sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==", + "dev": true, + "optional": true + }, + "esbuild-linux-riscv64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.5.tgz", + "integrity": "sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==", + "dev": true, + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.5.tgz", + "integrity": "sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.5.tgz", + "integrity": "sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.5.tgz", + "integrity": "sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.5.tgz", + "integrity": "sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==", + "dev": true, + "optional": true + }, + "esbuild-wasm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.5.tgz", + "integrity": "sha512-lTJOEKekN/4JI/eOEq0wLcx53co2N6vaT/XjBz46D1tvIVoUEyM0o2K6txW6gEotf31szFD/J1PbxmnbkGlK9A==", + "dev": true + }, + "esbuild-windows-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.5.tgz", + "integrity": "sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.5.tgz", + "integrity": "sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.5.tgz", + "integrity": "sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==", + "dev": true, + "optional": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "eventemitter-asyncresource": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "eventsource": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.2.tgz", + "integrity": "sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA==" + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fetch-cookie": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz", + "integrity": "sha512-BQm7iZLFhMWFy5CZ/162sAGjBfdNWb7a8LEqqnzsHFhxT/X/SVj/z2t2nu3aJvjlbQkrAlTUApplPRjWyH4mhA==", + "requires": { + "tough-cookie": "^2.3.3 || ^3.0.1 || ^4.0.0" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + } + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + } + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, + "hdr-histogram-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", + "dev": true, + "requires": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", + "dev": true + }, + "hosted-git-info": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.1.0.tgz", + "integrity": "sha512-Ek+QmMEqZF8XrbFdwoDjSbm7rT23pCgEMOJmz6GPk/s4yH//RQfNPArhIxbguNxROq/+5lNBwCDHMhA903Kx1Q==", + "dev": true, + "requires": { + "lru-cache": "^7.5.1" + } + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "dependencies": { + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dev": true, + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "requires": {} + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "ignore-walk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "dev": true, + "requires": { + "minimatch": "^5.0.1" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true + }, + "immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz", + "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==", + "dev": true + }, + "inquirer": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "dev": true + }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jasmine-core": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.1.1.tgz", + "integrity": "sha512-lmUfT5XcK9KKvt3lLYzn93hc4MGzlUBowExFVgzbSW0ZCrdeyS574dfsyfRhxbg81Wj4gk+RxUiTnj7KBfDA1g==", + "dev": true + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true + }, + "jsonc-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true + }, + "karma": { + "version": "6.3.20", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.20.tgz", + "integrity": "sha512-HRNQhMuKOwKpjYlWiJP0DUrJOh+QjaI/DTaD8b9rEm4Il3tJ8MijutVZH4ts10LuUFst/CedwTS6vieCN8yTSw==", + "dev": true, + "requires": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.4.1", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } + } + }, + "karma-chrome-launcher": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz", + "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==", + "dev": true, + "requires": { + "which": "^1.2.1" + } + }, + "karma-coverage": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.0.tgz", + "integrity": "sha512-gPVdoZBNDZ08UCzdMHHhEImKrw1+PAOQOIiffv1YsvxFhBjqvo/SVXNk4tqn1SYqX0BJZT6S/59zgxiBe+9OuA==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.0.5", + "minimatch": "^3.0.4" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "karma-jasmine": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.0.1.tgz", + "integrity": "sha512-FkL1Kk+JAKmim8VWU8RXKZBpl0lLI7J8LijM0/q7oP7emfB6QMZV1Az+JgqGKSLpF0tYaav+KUVFQroZUxQTHA==", + "dev": true, + "requires": { + "jasmine-core": "^4.1.0" + } + }, + "karma-jasmine-html-reporter": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz", + "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==", + "dev": true, + "requires": {} + }, + "karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "requires": { + "source-map-support": "^0.5.5" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true + }, + "less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "dev": true, + "requires": { + "copy-anything": "^2.0.1", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "parse-node-version": "^1.0.1", + "source-map": "~0.6.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "less-loader": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz", + "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==", + "dev": true, + "requires": { + "klona": "^2.0.4" + } + }, + "license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "requires": { + "webpack-sources": "^3.0.0" + } + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true + }, + "loader-utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", + "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "log4js": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.7.0.tgz", + "integrity": "sha512-KA0W9ffgNBLDj6fZCq/lRbgR6ABAodRIDHrZnS48vOtfKa4PzWImb0Md1lmGCdO3n3sbCm/n1/WmrNlZ8kCI3Q==", + "dev": true, + "requires": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.3" + } + }, + "lru-cache": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.0.tgz", + "integrity": "sha512-EIRtP1GrSJny0dqb50QXRUNBxHJhcpxHC++M5tD7RYbvLLn5KVWKsbyswSSqDuU15UFi3bgTQIY8nhDMeF6aDQ==", + "dev": true + }, + "magic-string": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz", + "integrity": "sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.8" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "dependencies": { + "@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "requires": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + } + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true + }, + "memfs": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", + "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", + "dev": true, + "requires": { + "fs-monkey": "^1.0.3" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "dev": true, + "requires": { + "schema-utils": "^4.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "minipass": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", + "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-json-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", + "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", + "dev": true, + "requires": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "requires": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + } + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true + }, + "needle": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.1.0.tgz", + "integrity": "sha512-gCE9weDhjVGCRqS8dwDR/D3GTAeyXLXuqp7I8EzH6DllZGXSUyxuqqLh+YX9rMAWaaTFyVAg6rHGL25dqvczKw==", + "dev": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "optional": true, + "requires": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true + }, + "node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "node-gyp-build": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", + "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==", + "dev": true, + "optional": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", + "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", + "dev": true, + "requires": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true + }, + "npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-install-checks": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", + "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==", + "dev": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "npm-package-arg": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.0.2.tgz", + "integrity": "sha512-v/miORuX8cndiOheW8p2moNuPJ7QhcFh9WGlTorruG8hXSA23vMTEp5hTCmDxic0nD8KHhj/NQgFuySD3GYY3g==", + "dev": true, + "requires": { + "hosted-git-info": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + } + }, + "npm-packlist": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", + "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", + "dev": true, + "requires": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "dependencies": { + "npm-bundled": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", + "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^2.0.0" + } + }, + "npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true + } + } + }, + "npm-pick-manifest": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz", + "integrity": "sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg==", + "dev": true, + "requires": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + } + }, + "npm-registry-fetch": { + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz", + "integrity": "sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw==", + "dev": true, + "requires": { + "make-fetch-happen": "^10.0.6", + "minipass": "^3.1.6", + "minipass-fetch": "^2.0.3", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^9.0.1", + "proc-log": "^2.0.0" + }, + "dependencies": { + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "requires": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + } + }, + "minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "requires": { + "encoding": "^0.1.13", + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + } + }, + "socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + } + } + } + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "requires": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + } + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "dependencies": { + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true + } + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pacote": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.3.0.tgz", + "integrity": "sha512-auhJAUlfC2TALo6I0s1vFoPvVFgWGx+uz/PnIojTTgkGwlK3Np8sGJ0ghfFhiuzJXTZoTycMLk8uLskdntPbDw==", + "dev": true, + "requires": { + "@npmcli/git": "^3.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/promise-spawn": "^3.0.0", + "@npmcli/run-script": "^3.0.1", + "cacache": "^16.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.6", + "mkdirp": "^1.0.4", + "npm-package-arg": "^9.0.0", + "npm-packlist": "^5.0.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^5.0.0", + "read-package-json-fast": "^2.0.3", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11" + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "parse5-html-rewriting-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "dev": true, + "requires": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + } + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + } + }, + "parse5-sax-parser": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + }, + "piscina": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", + "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", + "dev": true, + "requires": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0", + "nice-napi": "^1.0.2" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "postcss": { + "version": "8.4.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", + "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "dev": true, + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-properties": { + "version": "12.1.9", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.9.tgz", + "integrity": "sha512-/E7PRvK8DAVljBbeWrcEQJPG72jaImxF3vvCNFwv9cC8CzigVoNIpeyfnJzphnN3Fd8/auBf5wvkw6W9MfmTyg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "dev": true, + "requires": {} + }, + "postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "dev": true, + "requires": {} + }, + "postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-import": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.0.0.tgz", + "integrity": "sha512-Y20shPQ07RitgBGv2zvkEAu9bqvrD77C9axhj/aA1BQj4czape2MdClCExvB27EwYEJdGgKZBpKanb0t1rK2Kg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "dev": true, + "requires": {} + }, + "postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-loader": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", + "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.7" + } + }, + "postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "dev": true, + "requires": {} + }, + "postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "dev": true, + "requires": {} + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-nesting": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "dev": true, + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-opacity-percentage": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", + "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==", + "dev": true + }, + "postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "dev": true, + "requires": {} + }, + "postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-preset-env": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz", + "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==", + "dev": true, + "requires": { + "@csstools/postcss-cascade-layers": "^1.0.5", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.8", + "browserslist": "^4.21.3", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.0.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.8", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.10", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "dev": true, + "requires": {} + }, + "postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true + }, + "primeicons": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-6.0.1.tgz", + "integrity": "sha512-KDeO94CbWI4pKsPnYpA1FPjo79EsY9I+M8ywoPBSf9XMXoe/0crjbUK7jcQEDHuc0ZMRIZsxH3TYLv4TUtHmAA==" + }, + "primeng": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/primeng/-/primeng-14.1.2.tgz", + "integrity": "sha512-iLMeORiLD46SNAotxCwRXoaRMXLs3ZbFzyePrPSNAFQbKEbsLfpUvsAUAatb/TA0jd8TnXgdCjZ07ee4664XVQ==", + "requires": { + "tslib": "^2.3.0" + } + }, + "proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "requires": { + "pify": "^2.3.0" + } + }, + "read-package-json": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.2.tgz", + "integrity": "sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q==", + "dev": true, + "requires": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^2.3.1", + "normalize-package-data": "^4.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "dependencies": { + "npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true + } + } + }, + "read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "regexpu-core": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.1.tgz", + "integrity": "sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + } + }, + "regjsgen": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==", + "dev": true + }, + "regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "requires": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "requires": { + "tslib": "^2.1.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "devOptional": true + }, + "sass": { + "version": "1.54.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz", + "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-loader": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", + "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "dev": true, + "requires": { + "node-forge": "^1" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true + }, + "socket.io": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.2.tgz", + "integrity": "sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.2.0", + "socket.io-adapter": "~2.4.0", + "socket.io-parser": "~4.2.0" + } + }, + "socket.io-adapter": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", + "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", + "dev": true + }, + "socket.io-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", + "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "dev": true, + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + } + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "dev": true, + "requires": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + } + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "source-map-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz", + "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true + }, + "streamroller": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.3.tgz", + "integrity": "sha512-CphIJyFx2SALGHeINanjFRKQ4l7x2c+rXYJ4BMq0gd+ZK0gi4VT8b+eHe2wi58x4UayBAKx4xtHpXT/ea1cz8w==", + "dev": true, + "requires": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "stylus": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", + "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", + "dev": true, + "requires": { + "@adobe/css-tools": "^4.0.1", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "stylus-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.0.0.tgz", + "integrity": "sha512-WTbtLrNfOfLgzTaR9Lj/BPhQroKk/LC1hfTXSUbrxmxgfUo3Y3LpmKRVA2R1XbjvTAvOfaian9vOyfv1z99E+A==", + "dev": true, + "requires": { + "fast-glob": "^3.2.11", + "klona": "^2.0.5", + "normalize-path": "^3.0.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + } + }, + "terser-webpack-plugin": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.14", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.14.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, + "tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + } + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "ts-node": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", + "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", + "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", + "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", + "dev": true, + "requires": { + "builtins": "^5.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "dev": true + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "webpack": { + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dev": true, + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-dev-server": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz", + "integrity": "sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==", + "dev": true, + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "dependencies": { + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "ws": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.9.0.tgz", + "integrity": "sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==", + "dev": true, + "requires": {} + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true + }, + "webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "requires": { + "typed-assert": "^1.0.8" + } + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true, + "requires": {} + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", + "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "zone.js": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz", + "integrity": "sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==", + "requires": { + "tslib": "^2.3.0" + } + } + } +} diff --git a/kdb-web/package.json b/kdb-web/package.json new file mode 100644 index 00000000..5d0195d0 --- /dev/null +++ b/kdb-web/package.json @@ -0,0 +1,49 @@ +{ + "name": "kdb-web", + "version": "0.3.0", + "scripts": { + "ng": "ng", + "update-version": "ts-node -O '{\"module\": \"commonjs\"}' update-version.ts", + "prestart": "npm run update-version", + "start": "ng serve", + "prebuild": "npm run update-version", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "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", + "@auth0/angular-jwt": "^5.1.0", + "@microsoft/signalr": "^6.0.9", + "@ngx-translate/core": "^14.0.0", + "@ngx-translate/http-loader": "^7.0.0", + "primeicons": "^6.0.1", + "primeng": "^14.1.2", + "rxjs": "~7.5.0", + "tslib": "^2.3.0", + "zone.js": "~0.11.4" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^14.0.0", + "@angular/cli": "~14.0.0", + "@angular/compiler-cli": "^14.0.0", + "@types/jasmine": "~4.0.0", + "@types/node": "^18.8.3", + "jasmine-core": "~4.1.0", + "karma": "~6.3.0", + "karma-chrome-launcher": "~3.1.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.0.0", + "karma-jasmine-html-reporter": "~1.7.0", + "ts-node": "~8.3.0", + "typescript": "~4.7.2" + } +} diff --git a/kdb-web/src/app/app-routing.module.ts b/kdb-web/src/app/app-routing.module.ts new file mode 100644 index 00000000..803deb70 --- /dev/null +++ b/kdb-web/src/app/app-routing.module.ts @@ -0,0 +1,22 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { NotFoundComponent } from './components/error/not-found/not-found.component'; +import { AuthRoles } from './models/auth/auth-roles.enum'; +import { AuthGuard } from './modules/shared/guards/auth/auth.guard'; + +const routes: Routes = [ + { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, + { path: 'dashboard', loadChildren: () => import('./modules/view/dashboard/dashboard.module').then(m => m.DashboardModule), canActivate: [AuthGuard] }, + { path: 'change-password', loadChildren: () => import('./modules/view/change-password/change-password.module').then(m => m.ChangePasswordModule), canActivate: [AuthGuard] }, + { path: 'user-settings', loadChildren: () => import('./modules/view/user-settings/user-settings.module').then(m => m.UserSettingsModule), canActivate: [AuthGuard] }, + { path: 'auth', loadChildren: () => import('./modules/auth/auth.module').then(m => m.AuthModule) }, + { path: 'admin/settings', loadChildren: () => import('./modules/admin/settings/settings.module').then(m => m.SettingsModule), canActivate: [AuthGuard], data: { role: AuthRoles.Admin } }, + { path: 'admin/users', loadChildren: () => import('./modules/admin/auth-users/auth-user.module').then(m => m.AuthUserModule), canActivate: [AuthGuard], data: { role: AuthRoles.Admin } }, + { path: '404', component: NotFoundComponent} +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule] +}) +export class AppRoutingModule { } diff --git a/kdb-web/src/app/app.component.html b/kdb-web/src/app/app.component.html new file mode 100644 index 00000000..a9daaf72 --- /dev/null +++ b/kdb-web/src/app/app.component.html @@ -0,0 +1,36 @@ +
+ + + +
+
+ +
+
+
+ +
+
+
+ + +
+ + + + + + + + + +
+ + +
+
+
+ +
\ No newline at end of file diff --git a/kdb-web/src/app/app.component.scss b/kdb-web/src/app/app.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/app.component.spec.ts b/kdb-web/src/app/app.component.spec.ts new file mode 100644 index 00000000..3d9b4502 --- /dev/null +++ b/kdb-web/src/app/app.component.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + RouterTestingModule + ], + declarations: [ + AppComponent + ], + }).compileComponents(); + }); +}); diff --git a/kdb-web/src/app/app.component.ts b/kdb-web/src/app/app.component.ts new file mode 100644 index 00000000..90758e7d --- /dev/null +++ b/kdb-web/src/app/app.component.ts @@ -0,0 +1,21 @@ +import { Component, OnInit } from '@angular/core'; +import { AuthService } from './services/auth/auth.service'; +import { ThemeService } from './services/theme/theme.service'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'] +}) +export class AppComponent implements OnInit { + + constructor( + public authService: AuthService, + public themeService: ThemeService, + ) { } + + ngOnInit(): void { + this.themeService.loadTheme(); + this.themeService.loadMenu(); + } +} diff --git a/kdb-web/src/app/app.module.ts b/kdb-web/src/app/app.module.ts new file mode 100644 index 00000000..c8ae9aed --- /dev/null +++ b/kdb-web/src/app/app.module.ts @@ -0,0 +1,81 @@ +import { HttpClient } 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'; +import { JwtModule } from '@auth0/angular-jwt'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateHttpLoader } from '@ngx-translate/http-loader'; +import { ConfirmationService, MessageService } from 'primeng/api'; +import { DialogService } from 'primeng/dynamicdialog'; +import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; +import { FooterComponent } from './components/footer/footer.component'; +import { HeaderComponent } from './components/header/header.component'; +import { SidebarComponent } from './components/sidebar/sidebar.component'; +import { SpinnerComponent } from './components/spinner/spinner.component'; +import { SharedModule } from './modules/shared/shared.module'; +import { ErrorHandlerService } from './services/error-handler/error-handler.service'; +import { SettingsService } from './services/settings/settings.service'; +import { NotFoundComponent } from './components/error/not-found/not-found.component'; + +@NgModule({ + declarations: [ + AppComponent, + HeaderComponent, + SidebarComponent, + FooterComponent, + SpinnerComponent, + NotFoundComponent, + ], + imports: [ + BrowserModule, + BrowserAnimationsModule, + AppRoutingModule, + SharedModule, + JwtModule.forRoot({ + config: { + tokenGetter, + allowedDomains: ['localhost:5000', 'localhost:5001'], + disallowedRoutes: [] + } + }), + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [HttpClient] + } + }) + ], + providers: [ + { + provide: APP_INITIALIZER, + useFactory: configurationFactory, + deps: [SettingsService], + multi: true + }, + { + provide: ErrorHandler, + useClass: ErrorHandlerService + }, + MessageService, + ConfirmationService, + DialogService + ], + bootstrap: [AppComponent] +}) +export class AppModule { } + +export function configurationFactory(settingsService: SettingsService): () => Promise { + return (): Promise => { + return settingsService.loadSettings(); + }; +} + +export function tokenGetter(): string { + return localStorage.getItem('jwt') ?? ''; +} + +export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader { + return new TranslateHttpLoader(http); +} diff --git a/kdb-web/src/app/components/error/not-found/not-found.component.html b/kdb-web/src/app/components/error/not-found/not-found.component.html new file mode 100644 index 00000000..86079dc2 --- /dev/null +++ b/kdb-web/src/app/components/error/not-found/not-found.component.html @@ -0,0 +1,15 @@ +
+
+
+
+
+
+

+ {{'common.error' | translate}} +

+
+ +
+ {{'common.404' | translate}} +
+
\ No newline at end of file diff --git a/kdb-web/src/app/components/error/not-found/not-found.component.scss b/kdb-web/src/app/components/error/not-found/not-found.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/components/error/not-found/not-found.component.spec.ts b/kdb-web/src/app/components/error/not-found/not-found.component.spec.ts new file mode 100644 index 00000000..9d41c99a --- /dev/null +++ b/kdb-web/src/app/components/error/not-found/not-found.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NotFoundComponent } from './not-found.component'; + +describe('NotFoundComponent', () => { + let component: NotFoundComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ NotFoundComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(NotFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/components/error/not-found/not-found.component.ts b/kdb-web/src/app/components/error/not-found/not-found.component.ts new file mode 100644 index 00000000..9b4c5f59 --- /dev/null +++ b/kdb-web/src/app/components/error/not-found/not-found.component.ts @@ -0,0 +1,18 @@ +import { Location } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-not-found', + templateUrl: './not-found.component.html', + styleUrls: ['./not-found.component.scss'] +}) +export class NotFoundComponent implements OnInit { + + constructor( + public location: Location + ) { } + + ngOnInit(): void { + } + +} diff --git a/kdb-web/src/app/components/footer/footer.component.html b/kdb-web/src/app/components/footer/footer.component.html new file mode 100644 index 00000000..240f8583 --- /dev/null +++ b/kdb-web/src/app/components/footer/footer.component.html @@ -0,0 +1,26 @@ +
+
+
+ + {{'footer.frontend' | translate}}: + + + {{frontendVersion.getVersionString()}} + +
+ + | + +
+ + {{'footer.backend' | translate}}: + + + {{backendVersion.getVersionString()}} + +
+
+ +
\ No newline at end of file diff --git a/kdb-web/src/app/components/footer/footer.component.scss b/kdb-web/src/app/components/footer/footer.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/components/footer/footer.component.spec.ts b/kdb-web/src/app/components/footer/footer.component.spec.ts new file mode 100644 index 00000000..a3c4af95 --- /dev/null +++ b/kdb-web/src/app/components/footer/footer.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FooterComponent } from './footer.component'; + +describe('FooterComponent', () => { + let component: FooterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FooterComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(FooterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/components/footer/footer.component.ts b/kdb-web/src/app/components/footer/footer.component.ts new file mode 100644 index 00000000..bb2fa2eb --- /dev/null +++ b/kdb-web/src/app/components/footer/footer.component.ts @@ -0,0 +1,46 @@ +import { Component, OnInit } from '@angular/core'; +import { type } from 'os'; +import { catchError } from 'rxjs/operators'; +import { SoftwareVersion } from 'src/app/models/config/software-version'; +import { GuiService } from 'src/app/services/gui/gui.service'; +import { SettingsService } from 'src/app/services/settings/settings.service'; +import { SpinnerService } from 'src/app/services/spinner/spinner.service'; + +@Component({ + selector: 'app-footer', + templateUrl: './footer.component.html', + styleUrls: ['./footer.component.scss'] +}) +export class FooterComponent implements OnInit { + + + frontendVersion!: SoftwareVersion; + backendVersion!: SoftwareVersion; + + constructor( + private settings: SettingsService, + private guiService: GuiService, + private spinnerService: SpinnerService + ) { } + + ngOnInit(): void { + this.frontendVersion = this.settings.getWebVersion() ?? new SoftwareVersion('0', '0', '0'); + + this.spinnerService.showSpinner(); + this.guiService.getApiVersion() + .pipe(catchError(err => { + this.spinnerService.hideSpinner(); + throw err; + })) + .subscribe(version => { + this.spinnerService.hideSpinner(); + const webVersion = new SoftwareVersion( + version.major, + version.minor, + version.micro + ); + this.backendVersion = webVersion; + }); + } + +} diff --git a/kdb-web/src/app/components/header/header.component.html b/kdb-web/src/app/components/header/header.component.html new file mode 100644 index 00000000..d48d039c --- /dev/null +++ b/kdb-web/src/app/components/header/header.component.html @@ -0,0 +1,25 @@ +
+
+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
\ No newline at end of file diff --git a/kdb-web/src/app/components/header/header.component.scss b/kdb-web/src/app/components/header/header.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/components/header/header.component.spec.ts b/kdb-web/src/app/components/header/header.component.spec.ts new file mode 100644 index 00000000..381e8e80 --- /dev/null +++ b/kdb-web/src/app/components/header/header.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HeaderComponent } from './header.component'; + +describe('HeaderComponent', () => { + let component: HeaderComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ HeaderComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/components/header/header.component.ts b/kdb-web/src/app/components/header/header.component.ts new file mode 100644 index 00000000..6d4ad0ee --- /dev/null +++ b/kdb-web/src/app/components/header/header.component.ts @@ -0,0 +1,168 @@ +import { Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { Router } from '@angular/router'; +import { LangChangeEvent, TranslateService } from '@ngx-translate/core'; +import { MenuItem, PrimeNGConfig } from 'primeng/api'; +import { catchError } from 'rxjs/operators'; +import { AuthService } from 'src/app/services/auth/auth.service'; +import { SettingsService } from 'src/app/services/settings/settings.service'; +import { SpinnerService } from 'src/app/services/spinner/spinner.service'; +import { ThemeService } from 'src/app/services/theme/theme.service'; + +@Component({ + selector: 'app-header', + templateUrl: './header.component.html', + styleUrls: ['./header.component.scss'] +}) +export class HeaderComponent implements OnInit { + @Output() isSidebarFullWidth: EventEmitter = new EventEmitter(this.themeService.isSidebarOpen); + + langList: MenuItem[] = []; + themeList: MenuItem[] = []; + userMenuList!: MenuItem[]; + + constructor( + private authService: AuthService, + private router: Router, + private themeService: ThemeService, + private spinnerService: SpinnerService, + private settings: SettingsService, + private translateService: TranslateService, + private config: PrimeNGConfig + ) { } + + ngOnInit(): void { + this.translateService.setDefaultLang('en'); + this.initMenuLists(); + this.loadLang(); + this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { + this.initUserMenuList(); + }); + } + + initUserMenuList(): void { + this.spinnerService.showSpinner(); + const mail = this.authService.getEMailFromDecodedToken(this.authService.getDecodedToken()); + this.authService.getUserByEMail(mail ?? '') + .pipe(catchError(err => { + this.spinnerService.hideSpinner(); + this.authService.logout(); + throw err; + })) + .subscribe(user => { + this.spinnerService.hideSpinner(); + + + this.userMenuList = [ + { + label: `${user.firstName} ${user.lastName}`, + disabled: true + }, + { + separator: true + }, + { + label: this.translateService.instant('header.change_password'), command: () => { + this.changePassword(); + }, + icon: 'pi pi-key' + }, + { + label: this.translateService.instant('header.settings'), command: () => { + this.userSettings(); + }, + icon: 'pi pi-cog' + }, + { + label: this.translateService.instant('header.logout'), command: () => { + this.logout(); + }, + icon: 'pi pi-sign-out' + } + ]; + }); + } + + initMenuLists(): void { + this.langList = [ + { + label: 'English', command: () => { + this.translate('en'); + this.setLang('en'); + }, + }, + { + label: 'Deutsch', command: () => { + this.translate('de'); + this.setLang('de'); + }, + }, + ]; + + this.initUserMenuList(); + + this.settings.getThemes()?.forEach(theme => { + this.themeList.push({ + label: theme.Label, + command: () => { + this.changeTheme(theme.Name); + } + }); + }); + } + + toggleMenu(): void { + this.themeService.setIsMenuOpen(!this.themeService.isSidebarOpen); + this.isSidebarFullWidth.emit(this.themeService.isSidebarOpen); + } + + changeTheme(name: string): void { + this.themeService.setTheme(name); + } + + changePassword(): void { + this.router.navigate(['/change-password']); + } + + userSettings(): void { + this.router.navigate(['/user-settings']); + } + + logout(): void { + this.authService.logout(); + } + + translate(lang: string) { + this.translateService.use(lang); + this.translateService.get('primeng').subscribe(res => this.config.setTranslation(res)); + } + + loadLang(): void { + const token = this.authService.getDecodedToken(); + const mail = this.authService.getEMailFromDecodedToken(token); + + if (!mail) { + this.translate('en'); + return; + } + + let lang = localStorage.getItem(`${mail}_lang`); + if (!lang) { + lang = 'en'; + this.setLang(lang); + } + this.translate(lang); + } + + setLang(lang: string): void { + this.authService.isUserLoggedInAsync().then(result => { + if (!result) { + return; + } + + const token = this.authService.getDecodedToken(); + const mail = this.authService.getEMailFromDecodedToken(token); + localStorage.setItem(`${mail}_lang`, lang); + }); + } + +} diff --git a/kdb-web/src/app/components/sidebar/sidebar.component.html b/kdb-web/src/app/components/sidebar/sidebar.component.html new file mode 100644 index 00000000..b3bb4f3b --- /dev/null +++ b/kdb-web/src/app/components/sidebar/sidebar.component.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/kdb-web/src/app/components/sidebar/sidebar.component.scss b/kdb-web/src/app/components/sidebar/sidebar.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/components/sidebar/sidebar.component.spec.ts b/kdb-web/src/app/components/sidebar/sidebar.component.spec.ts new file mode 100644 index 00000000..1f932e20 --- /dev/null +++ b/kdb-web/src/app/components/sidebar/sidebar.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SidebarComponent } from './sidebar.component'; + +describe('SidebarComponent', () => { + let component: SidebarComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ SidebarComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SidebarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/components/sidebar/sidebar.component.ts b/kdb-web/src/app/components/sidebar/sidebar.component.ts new file mode 100644 index 00000000..a87eb35f --- /dev/null +++ b/kdb-web/src/app/components/sidebar/sidebar.component.ts @@ -0,0 +1,52 @@ +import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { LangChangeEvent, TranslateService } from '@ngx-translate/core'; +import { MenuItem } from 'primeng/api'; +import { AuthRoles } from 'src/app/models/auth/auth-roles.enum'; +import { AuthService } from 'src/app/services/auth/auth.service'; + +@Component({ + selector: 'app-sidebar', + templateUrl: './sidebar.component.html', + styleUrls: ['./sidebar.component.scss'] +}) +export class SidebarComponent implements OnInit, OnChanges { + + @Input() isSidebarOpen!: boolean; + + menuItems!: MenuItem[]; + + constructor( + private authService: AuthService, + private translateService: TranslateService + ) { } + + ngOnInit(): void { + this.translateService.onLangChange.subscribe(async (event: LangChangeEvent) => { + await this.setMenu(this.isSidebarOpen); + }); + } + + async setMenu(isSidebarOpen: boolean) { + this.menuItems = []; + this.menuItems = [ + { label: isSidebarOpen ? this.translateService.instant('sidebar.dashboard') : '', icon: 'pi pi-th-large', routerLink: 'dashboard' }, + ]; + + if (await this.authService.hasUserPermission(AuthRoles.Admin)) { + this.menuItems.push( + { separator: true }, + { label: isSidebarOpen ? this.translateService.instant('sidebar.config') : '', icon: 'pi pi-cog', routerLink: '/admin/settings' }, + { label: isSidebarOpen ? this.translateService.instant('sidebar.auth_user_list') : '', icon: 'pi pi-user-edit', routerLink: '/admin/users' }, + ); + this.menuItems = this.menuItems.slice(); + } + } + + async ngOnChanges(changes: SimpleChanges): Promise { + if (!changes) + return; + + await this.setMenu(changes['isSidebarOpen'].currentValue); + } + +} diff --git a/kdb-web/src/app/components/spinner/spinner.component.html b/kdb-web/src/app/components/spinner/spinner.component.html new file mode 100644 index 00000000..b4f52b87 --- /dev/null +++ b/kdb-web/src/app/components/spinner/spinner.component.html @@ -0,0 +1,7 @@ + +
+
+ +
+
+
\ No newline at end of file diff --git a/kdb-web/src/app/components/spinner/spinner.component.scss b/kdb-web/src/app/components/spinner/spinner.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/components/spinner/spinner.component.spec.ts b/kdb-web/src/app/components/spinner/spinner.component.spec.ts new file mode 100644 index 00000000..061f78d5 --- /dev/null +++ b/kdb-web/src/app/components/spinner/spinner.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SpinnerComponent } from './spinner.component'; + +describe('SpinnerComponent', () => { + let component: SpinnerComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ SpinnerComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SpinnerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/components/spinner/spinner.component.ts b/kdb-web/src/app/components/spinner/spinner.component.ts new file mode 100644 index 00000000..4753534c --- /dev/null +++ b/kdb-web/src/app/components/spinner/spinner.component.ts @@ -0,0 +1,18 @@ +import { Component, OnInit } from '@angular/core'; +import { SpinnerService } from 'src/app/services/spinner/spinner.service'; + +@Component({ + selector: 'app-spinner', + templateUrl: './spinner.component.html', + styleUrls: ['./spinner.component.scss'] +}) +export class SpinnerComponent implements OnInit { + + constructor( + public spinnerService: SpinnerService + ) { } + + ngOnInit(): void { + } + +} diff --git a/kdb-web/src/app/models/auth/admin-update-user.dto.ts b/kdb-web/src/app/models/auth/admin-update-user.dto.ts new file mode 100644 index 00000000..fe3222f5 --- /dev/null +++ b/kdb-web/src/app/models/auth/admin-update-user.dto.ts @@ -0,0 +1,7 @@ +import { AuthUserDTO } from "./auth-user.dto"; + +export interface AdminUpdateUserDTO { + authUserDTO: AuthUserDTO; + newAuthUserDTO: AuthUserDTO; + changePassword: boolean; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/auth/auth-error-messages.enum.ts b/kdb-web/src/app/models/auth/auth-error-messages.enum.ts new file mode 100644 index 00000000..4c3ebc39 --- /dev/null +++ b/kdb-web/src/app/models/auth/auth-error-messages.enum.ts @@ -0,0 +1,7 @@ +export enum AuthErrorMessages { + UserIsEmpty = "User is empty", + UserNotFound = "User not found", + WrongPassword = "Wrong password", + UserAlreadyExists = "User already exists", + EMailNotConfirmed = "E-Mail not confirmed" +} \ No newline at end of file diff --git a/kdb-web/src/app/models/auth/auth-roles.enum.ts b/kdb-web/src/app/models/auth/auth-roles.enum.ts new file mode 100644 index 00000000..1bf2166d --- /dev/null +++ b/kdb-web/src/app/models/auth/auth-roles.enum.ts @@ -0,0 +1,4 @@ +export enum AuthRoles { + Normal = 0, + Admin = 1 +} diff --git a/kdb-web/src/app/models/auth/auth-user-atr-errors.ts b/kdb-web/src/app/models/auth/auth-user-atr-errors.ts new file mode 100644 index 00000000..09394d9b --- /dev/null +++ b/kdb-web/src/app/models/auth/auth-user-atr-errors.ts @@ -0,0 +1,12 @@ +export class AuthUserAtrErrors { + firstName: AuthUserAtrErrorType = new AuthUserAtrErrorType(); + lastName: AuthUserAtrErrorType = new AuthUserAtrErrorType(); + email: AuthUserAtrErrorType = new AuthUserAtrErrorType(); + password: AuthUserAtrErrorType = new AuthUserAtrErrorType(); +} + +export class AuthUserAtrErrorType { + wrongData: boolean = false; + required: boolean = false; + notConfirmed: boolean = false; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/auth/auth-user.dto.ts b/kdb-web/src/app/models/auth/auth-user.dto.ts new file mode 100644 index 00000000..17604288 --- /dev/null +++ b/kdb-web/src/app/models/auth/auth-user.dto.ts @@ -0,0 +1,11 @@ +import { AuthRoles } from "./auth-roles.enum"; + +export interface AuthUserDTO { + id?: number; + firstName: string | null; + lastName: string | null; + email: string | null; + password: string | null; + isConfirmed?: boolean + authRole?: AuthRoles; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/auth/email-string.dto.ts b/kdb-web/src/app/models/auth/email-string.dto.ts new file mode 100644 index 00000000..ffe000b8 --- /dev/null +++ b/kdb-web/src/app/models/auth/email-string.dto.ts @@ -0,0 +1,3 @@ +export interface EMailStringDTO { + email: string; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/auth/register-error-messages.enum.ts b/kdb-web/src/app/models/auth/register-error-messages.enum.ts new file mode 100644 index 00000000..31ffc509 --- /dev/null +++ b/kdb-web/src/app/models/auth/register-error-messages.enum.ts @@ -0,0 +1,4 @@ +export enum RegisterErrorMessages { + InvalidEMail = "Invalid E-Mail", + UserAlreadyExists = "User already exists", +} \ No newline at end of file diff --git a/kdb-web/src/app/models/auth/reset-password.dto.ts b/kdb-web/src/app/models/auth/reset-password.dto.ts new file mode 100644 index 00000000..669b349d --- /dev/null +++ b/kdb-web/src/app/models/auth/reset-password.dto.ts @@ -0,0 +1,6 @@ +import { AuthUserDTO } from "./auth-user.dto"; + +export interface ResetPasswordDTO { + id: string; + password: string; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/auth/token.dto.ts b/kdb-web/src/app/models/auth/token.dto.ts new file mode 100644 index 00000000..10e8b7c4 --- /dev/null +++ b/kdb-web/src/app/models/auth/token.dto.ts @@ -0,0 +1,4 @@ +export interface TokenDTO { + token: string; + refreshToken: string; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/auth/update-user.dto.ts b/kdb-web/src/app/models/auth/update-user.dto.ts new file mode 100644 index 00000000..15964af6 --- /dev/null +++ b/kdb-web/src/app/models/auth/update-user.dto.ts @@ -0,0 +1,6 @@ +import { AuthUserDTO } from "./auth-user.dto"; + +export interface UpdateUserDTO { + authUserDTO: AuthUserDTO; + newAuthUserDTO: AuthUserDTO; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/config/api-version.ts b/kdb-web/src/app/models/config/api-version.ts new file mode 100644 index 00000000..2de63b33 --- /dev/null +++ b/kdb-web/src/app/models/config/api-version.ts @@ -0,0 +1,5 @@ +export interface ApiVersion { + Major: string; + Minor: string; + Micro: string; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/config/appsettings.ts b/kdb-web/src/app/models/config/appsettings.ts new file mode 100644 index 00000000..de434a03 --- /dev/null +++ b/kdb-web/src/app/models/config/appsettings.ts @@ -0,0 +1,8 @@ +import { SoftwareVersion } from "./software-version"; +import { Theme } from '../view/theme'; + +export interface Appsettings { + ApiURL: string; + WebVersion: SoftwareVersion; + Themes: Theme[]; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/config/settings.dto.ts b/kdb-web/src/app/models/config/settings.dto.ts new file mode 100644 index 00000000..825457c6 --- /dev/null +++ b/kdb-web/src/app/models/config/settings.dto.ts @@ -0,0 +1,16 @@ +export interface SettingsDTO { + webVersion: string; + apiVersion: string; + configPath: string; + webBaseURL: string; + apiBaseURL: string; + + tokenExpireTime: number; + refreshTokenExpireTime: number; + + mailUser: string; + mailPort: number; + mailHost: string; + mailTransceiver: string; + mailTransceiverAddress: string; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/config/software-version.dto.ts b/kdb-web/src/app/models/config/software-version.dto.ts new file mode 100644 index 00000000..6a6d51bd --- /dev/null +++ b/kdb-web/src/app/models/config/software-version.dto.ts @@ -0,0 +1,5 @@ +export interface SoftwareVersionDTO { + major: string; + minor: string; + micro: string +} \ No newline at end of file diff --git a/kdb-web/src/app/models/config/software-version.ts b/kdb-web/src/app/models/config/software-version.ts new file mode 100644 index 00000000..4025b2b7 --- /dev/null +++ b/kdb-web/src/app/models/config/software-version.ts @@ -0,0 +1,19 @@ +export class SoftwareVersion { + Major: string; + Minor: string; + Micro: string; + + constructor( + major: string, + minor: string, + micro: string + ) { + this.Major = major; + this.Minor = minor; + this.Micro = micro; + } + + getVersionString(): string { + return `${this.Major}.${this.Minor}.${this.Micro}`; + } +} \ No newline at end of file diff --git a/kdb-web/src/app/models/error/error-dto.ts b/kdb-web/src/app/models/error/error-dto.ts new file mode 100644 index 00000000..14bffe77 --- /dev/null +++ b/kdb-web/src/app/models/error/error-dto.ts @@ -0,0 +1,8 @@ +import { ServiceErrorCode } from "./service-error-code.enum"; + + +export class ErrorDTO { + errorCode!: ServiceErrorCode; + message!: string; + +} diff --git a/kdb-web/src/app/models/error/service-error-code.enum.ts b/kdb-web/src/app/models/error/service-error-code.enum.ts new file mode 100644 index 00000000..5853b5d8 --- /dev/null +++ b/kdb-web/src/app/models/error/service-error-code.enum.ts @@ -0,0 +1,16 @@ +export enum ServiceErrorCode { + Unknown = 0, + + InvalidDependencies = 1, + InvalidData = 2, + NotFound = 3, + DataAlreadyExists = 4, + UnableToAdd = 5, + UnableToDelete = 6, + + InvalidUser = 7, + + ConnectionFailed = 8, + Timeout = 9, + MailError = 10 +} diff --git a/kdb-web/src/app/models/selection/auth-user/auth-user-select-criterion.dto.ts b/kdb-web/src/app/models/selection/auth-user/auth-user-select-criterion.dto.ts new file mode 100644 index 00000000..a0e24552 --- /dev/null +++ b/kdb-web/src/app/models/selection/auth-user/auth-user-select-criterion.dto.ts @@ -0,0 +1,8 @@ +import { SelectCriterion } from "../select-criterion.model"; + +export interface AuthUserSelectCriterion extends SelectCriterion { + firstName: string | null; + lastName: string | null; + email: string | null; + authRole?: number | null; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/selection/auth-user/get-filtered-auth-users-result.dto.ts b/kdb-web/src/app/models/selection/auth-user/get-filtered-auth-users-result.dto.ts new file mode 100644 index 00000000..23b8e9f8 --- /dev/null +++ b/kdb-web/src/app/models/selection/auth-user/get-filtered-auth-users-result.dto.ts @@ -0,0 +1,6 @@ +import { AuthUserDTO } from "../../auth/auth-user.dto"; + +export interface GetFilteredAuthUsersResultDTO { + users: AuthUserDTO[]; + totalCount: number; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/selection/logins/login-select-criterion.dto.ts b/kdb-web/src/app/models/selection/logins/login-select-criterion.dto.ts new file mode 100644 index 00000000..e1998d60 --- /dev/null +++ b/kdb-web/src/app/models/selection/logins/login-select-criterion.dto.ts @@ -0,0 +1,8 @@ +import { SelectCriterion } from "../select-criterion.model"; + +export interface LoginSelectCriterion extends SelectCriterion { + timeFrom: string; + timeTo: string; + userName: string; + hostName: string; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/selection/select-criterion.model.ts b/kdb-web/src/app/models/selection/select-criterion.model.ts new file mode 100644 index 00000000..e6d29f3b --- /dev/null +++ b/kdb-web/src/app/models/selection/select-criterion.model.ts @@ -0,0 +1,6 @@ +export interface SelectCriterion { + pageIndex: number; + pageSize: number; + sortDirection: string | null; + sortColumn: string | null; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/utils/confirmation-dialog.ts b/kdb-web/src/app/models/utils/confirmation-dialog.ts new file mode 100644 index 00000000..8763a78f --- /dev/null +++ b/kdb-web/src/app/models/utils/confirmation-dialog.ts @@ -0,0 +1,7 @@ +export interface ConfirmationDialog { + key?: string; + header: string; + message: string; + accept?: () => void; + reject?: () => void; +} diff --git a/kdb-web/src/app/models/utils/toast-options.ts b/kdb-web/src/app/models/utils/toast-options.ts new file mode 100644 index 00000000..bde79a1b --- /dev/null +++ b/kdb-web/src/app/models/utils/toast-options.ts @@ -0,0 +1,5 @@ +export interface ToastOptions { + life?: number; + sticky?: boolean; + closable?: boolean; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/view/theme.ts b/kdb-web/src/app/models/view/theme.ts new file mode 100644 index 00000000..f918b2c3 --- /dev/null +++ b/kdb-web/src/app/models/view/theme.ts @@ -0,0 +1,4 @@ +export interface Theme { + Label: string; + Name: string; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/view/themes.enum.ts b/kdb-web/src/app/models/view/themes.enum.ts new file mode 100644 index 00000000..4aae8a53 --- /dev/null +++ b/kdb-web/src/app/models/view/themes.enum.ts @@ -0,0 +1,7 @@ +export enum Themes { + Default = "sh-edraft-dark-theme", + DefaultLight = "default-light-theme", + DefaultDark = "default-dark-theme", + ShEdraftLight = "sh-edraft-light-theme", + ShEdraftDark = "sh-edraft-dark-theme", +} \ No newline at end of file diff --git a/kdb-web/src/app/modules/admin/auth-users/auth-user-routing.module.ts b/kdb-web/src/app/modules/admin/auth-users/auth-user-routing.module.ts new file mode 100644 index 00000000..3a10e506 --- /dev/null +++ b/kdb-web/src/app/modules/admin/auth-users/auth-user-routing.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { AuthUserComponent } from './components/auth-user/auth-user.component'; + +const routes: Routes = [ + {path: '', component: AuthUserComponent} +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class AuthUserRoutingModule { } diff --git a/kdb-web/src/app/modules/admin/auth-users/auth-user.module.ts b/kdb-web/src/app/modules/admin/auth-users/auth-user.module.ts new file mode 100644 index 00000000..c59463ac --- /dev/null +++ b/kdb-web/src/app/modules/admin/auth-users/auth-user.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { AuthUserRoutingModule } from './auth-user-routing.module'; +import { AuthUserComponent } from './components/auth-user/auth-user.component'; +import { SharedModule } from '../../shared/shared.module'; + + +@NgModule({ + declarations: [ + AuthUserComponent + ], + imports: [ + CommonModule, + AuthUserRoutingModule, + SharedModule + ] +}) +export class AuthUserModule { } diff --git a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html new file mode 100644 index 00000000..1cb83a55 --- /dev/null +++ b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html @@ -0,0 +1,201 @@ +

+ {{'admin.auth_users.header' | translate}} +

+
+
+ + + +
+
+ {{users.length}} {{'admin.auth_users.of' | translate}} + {{dt.totalRecords}} + + {{'admin.auth_users.users' | translate}} +
+ +
+ + +
+
+
+ + + + +
+
{{'admin.auth_users.headers.first_name' | translate}}
+ +
+ + + +
+
{{'admin.auth_users.headers.last_name' | translate}}
+ +
+ + + +
+
{{'admin.auth_users.headers.e_mail' | translate}}
+ +
+ + + +
+
{{'admin.auth_users.headers.active' | translate}}
+ +
+ + + +
+
{{'admin.auth_users.headers.role' | translate}}
+ +
+ + + +
+
{{'admin.auth_users.headers.password' | translate}}
+
+ + + +
+
{{'admin.auth_users.headers.actions' | translate}}
+
+ + + + +
+ +
+ + +
+ +
+ + +
+ +
+ + + +
+ +
+ + + + +
+ + + + + + + + + + {{user.firstName}} + + + + + + + + + + {{user.lastName}} + + + + + + + + + + {{user.email}} + + + + + + + + + + + + + + + + + + + + + + {{user.authRole | authRole}} + + + + + + + + + + + + + +
+ + + + + +
+ + +
+ + + + {{'admin.auth_users.no_entries_found' | translate}} + + + + + +
+
+
\ No newline at end of file diff --git a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.scss b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.spec.ts b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.spec.ts new file mode 100644 index 00000000..97053614 --- /dev/null +++ b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AuthUserComponent } from './auth-user.component'; + +describe('AuthUserComponent', () => { + let component: AuthUserComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AuthUserComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AuthUserComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts new file mode 100644 index 00000000..598b18d4 --- /dev/null +++ b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts @@ -0,0 +1,330 @@ +import { Component, OnInit } from '@angular/core'; +import { catchError, debounceTime, last } from 'rxjs/operators'; +import { AuthRoles } from 'src/app/models/auth/auth-roles.enum'; +import { AuthUserDTO } from 'src/app/models/auth/auth-user.dto'; +import { AuthService } from 'src/app/services/auth/auth.service'; +import { ConfirmationDialogService } from 'src/app/services/confirmation-dialog/confirmation-dialog.service'; +import { SpinnerService } from 'src/app/services/spinner/spinner.service'; +import { ToastService } from 'src/app/services/toast/toast.service'; +import { Table } from 'primeng/table'; +import { ServiceErrorCode } from 'src/app/models/error/service-error-code.enum'; +import { RegisterErrorMessages } from 'src/app/models/auth/register-error-messages.enum'; +import { ErrorDTO } from 'src/app/models/error/error-dto'; +import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; +import { AuthUserSelectCriterion } from 'src/app/models/selection/auth-user/auth-user-select-criterion.dto'; +import { LazyLoadEvent } from 'primeng/api'; +import { throwError } from 'rxjs'; +import { TranslateService } from '@ngx-translate/core'; + + +@Component({ + selector: 'app-auth-user', + templateUrl: './auth-user.component.html', + styleUrls: ['./auth-user.component.scss'] +}) +export class AuthUserComponent implements OnInit { + + users!: AuthUserDTO[]; + statuses!: any[]; + loading = true; + activityValues: number[] = [0, 100]; + + clonedUsers: { [s: string]: AuthUserDTO; } = {}; + isEditingNew: boolean = false; + + authRoles = [ + { label: AuthRoles[AuthRoles.Normal].toString(), value: AuthRoles.Normal }, + { label: AuthRoles[AuthRoles.Admin].toString(), value: AuthRoles.Admin } + ] + + newUserTemplate: AuthUserDTO = { + id: 0, + firstName: "", + lastName: "", + email: "", + password: "", + authRole: AuthRoles.Normal + }; + + isFirstNameInvalid: boolean = false; + isLastNameInvalid: boolean = false; + isEMailInvalid: boolean = false; + isPasswordInvalid: boolean = false; + + loggedInUserEMail: string = ""; + + filterForm!: FormGroup<{ + firstName: FormControl, + lastName: FormControl, + email: FormControl, + authRole: FormControl + }>; + searchCriterions!: AuthUserSelectCriterion; + totalRecords!: number; + + constructor( + private authService: AuthService, + private spinnerService: SpinnerService, + private toastService: ToastService, + private confirmDialog: ConfirmationDialogService, + private fb: FormBuilder, + private translate: TranslateService + ) { } + + ngOnInit(): void { + this.loggedInUserEMail = this.authService.getEMailFromDecodedToken(this.authService.getDecodedToken()) ?? ''; + this.searchCriterions = { + firstName: null, + lastName: null, + email: null, + authRole: null, + pageIndex: 0, + pageSize: 10, + sortColumn: null, + sortDirection: null + }; + + this.setFilterForm(); + // this.loadNextPage(); + } + + setFilterForm() { + this.filterForm = this.fb.group({ + firstName: [''], + lastName: [''], + email: [''], + authRole: [''] + }); + + this.filterForm.valueChanges.pipe( + debounceTime(600) + ).subscribe(changes => { + if (changes.firstName) { + this.searchCriterions.firstName = changes.firstName; + } else { + this.searchCriterions.firstName = null; + } + + if (changes.lastName) { + this.searchCriterions.lastName = changes.lastName; + } else { + this.searchCriterions.lastName = null; + } + + if (changes.email) { + this.searchCriterions.email = changes.email; + } else { + this.searchCriterions.email = null; + } + + if (changes.authRole != null) { + this.searchCriterions.authRole = +changes.authRole; + } else { + this.searchCriterions.authRole = null; + } + + if (this.searchCriterions.pageSize) + this.searchCriterions.pageSize = 10; + + if (this.searchCriterions.pageSize) + this.searchCriterions.pageIndex = 0; + + this.loadNextPage(); + }); + } + + loadNextPage() { + this.authService.getFilteredUsers(this.searchCriterions).pipe(catchError(err => { + this.loading = false; + return throwError(err); + })).subscribe(list => { + this.totalRecords = list.totalCount; + this.users = list.users; + this.loading = false; + }); + } + + nextPage(event: LazyLoadEvent) { + this.searchCriterions.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'; + + if (event.filters) { + // + "" => convert to string + this.searchCriterions.firstName = event.filters['firstName'] ? event.filters['firstName'] + "" : null; + this.searchCriterions.lastName = event.filters['lastName'] ? event.filters['lastName'] + "" : null; + this.searchCriterions.email = event.filters['email'] ? event.filters['email'] + "" : null; + this.searchCriterions.authRole = event.filters['authRole'] ? +event.filters['authRole'] : null; + } + + this.loadNextPage(); + } + + resetFilters(table: Table) { + this.filterForm.reset(); + } + + initUserList(): void { + this.spinnerService.showSpinner(); + this.authService.getAllUsers() + .pipe(catchError(err => { + this.spinnerService.hideSpinner(); + throw err; + })) + .subscribe(users => { + this.users = users; + this.spinnerService.hideSpinner(); + }); + } + + onRowEditInit(table: Table, user: AuthUserDTO, index: number) { + this.clonedUsers[index] = { ...user }; + } + + onRowEditSave(table: Table, newUser: AuthUserDTO, index: number) { + const oldUser = this.clonedUsers[index]; + delete this.clonedUsers[index]; + + if (JSON.stringify(oldUser) === JSON.stringify(newUser) && !this.isEditingNew) { + return; + } + + if (this.isEditingNew && JSON.stringify(newUser) === JSON.stringify(this.newUserTemplate)) { + this.isEditingNew = false; + this.users.splice(index, 1); + return; + } + + this.isFirstNameInvalid = newUser.firstName == ""; + this.isLastNameInvalid = newUser.lastName == ""; + this.isEMailInvalid = newUser.email == ""; + this.isPasswordInvalid = newUser.password == ""; + + if ( + this.isEditingNew && ( + newUser.firstName == "" || + newUser.lastName == "" || + newUser.email == "" + ) + ) { + table.initRowEdit(newUser); + return; + } + + if (this.isEditingNew) { + this.spinnerService.showSpinner(); + this.authService.register(newUser).pipe(catchError(error => { + this.spinnerService.hideSpinner(); + + if (error.error !== null) { + const err: ErrorDTO = error.error; + + if (err.errorCode === ServiceErrorCode.InvalidData && err.message === RegisterErrorMessages.InvalidEMail) { + this.isEMailInvalid = true; + this.toastService.error(this.translate.instant('admin.auth_users.message.invalid_email'), this.translate.instant('admin.auth_users.message.invalid_email_d', { email: newUser.email })); + } else if (err.errorCode === ServiceErrorCode.InvalidUser && err.message === RegisterErrorMessages.UserAlreadyExists) { + this.isEMailInvalid = true; + this.toastService.error(this.translate.instant('admin.auth_users.message.user_already_exists'), this.translate.instant('admin.auth_users.message.user_already_exists_d', { email: newUser.email })); + } + error.error = null; + table.initRowEdit(newUser); + } + this.spinnerService.hideSpinner(); + + throw error; + })) + .subscribe(_ => { + this.initUserList(); + this.spinnerService.hideSpinner(); + this.toastService.success(this.translate.instant('admin.auth_users.message.user_added'), this.translate.instant('admin.auth_users.message.user_added_d', { email: newUser.email })); + this.isEditingNew = false; + }); + this.triggerUserChangeDetection(); + return; + } + + this.spinnerService.showSpinner(); + this.authService.updateUserAsAdmin({ + authUserDTO: oldUser, + newAuthUserDTO: newUser, + changePassword: newUser.password != "" + }).pipe(catchError(err => { + this.spinnerService.hideSpinner(); + this.toastService.error(this.translate.instant('admin.auth_users.message.user_change_failed'), this.translate.instant('admin.auth_users.message.user_change_failed_d', { email: newUser.email })); + this.initUserList(); + throw err; + })) + .subscribe(_ => { + this.initUserList(); + this.spinnerService.hideSpinner(); + this.toastService.success(this.translate.instant('admin.auth_users.message.user_changed'), this.translate.instant('admin.auth_users.message.user_changed_d', { email: newUser.email })); + }); + this.triggerUserChangeDetection(); + } + + onRowEditCancel(user: AuthUserDTO, index: number) { + this.isFirstNameInvalid = false; + this.isLastNameInvalid = false; + this.isEMailInvalid = false; + this.isPasswordInvalid = false; + + if (this.isEditingNew) { + this.users.splice(index, 1); + this.triggerUserChangeDetection(); + delete this.clonedUsers[index]; + this.isEditingNew = false; + return; + } + + this.users[index] = this.clonedUsers[index]; + this.triggerUserChangeDetection(); + delete this.clonedUsers[index]; + } + + deleteUser(user: AuthUserDTO) { + if (user.email == this.loggedInUserEMail) { + this.toastService.error(this.translate.instant('admin.auth_users.message.cannot_delete_user'), this.translate.instant('admin.auth_users.message.logon_with_another_user')); + return; + } + + this.confirmDialog.confirmDialog( + this.translate.instant('admin.auth_users.message.user_delete'), this.translate.instant('admin.auth_users.message.user_delete_q', { email: user.email }), + () => { + this.spinnerService.showSpinner(); + this.authService.deleteUserByMail(user.email ?? '') + .pipe(catchError(err => { + this.spinnerService.hideSpinner(); + throw err; + })) + .subscribe(_ => { + this.initUserList(); + this.spinnerService.hideSpinner(); + this.toastService.success(this.translate.instant('admin.auth_users.message.user_deleted'), this.translate.instant('admin.auth_users.message.user_deleted_d', { email: user.email })); + }); + }); + } + + addUser(table: Table) { + const newUser = JSON.parse(JSON.stringify(this.newUserTemplate)); + newUser.id = Math.max.apply(Math, this.users.map(u => { return u.id ?? 0; })) + 1; + console.log(newUser); + + this.users.push(newUser); + this.triggerUserChangeDetection(); + + table.initRowEdit(newUser); + + const index = this.users.findIndex(u => u.email == newUser.email); + this.onRowEditInit(table, newUser, index); + + this.isEditingNew = true; + } + + triggerUserChangeDetection() { + // trigger change detection (https://github.com/primefaces/primeng/issues/2219) + this.users = this.users.slice(); + } + +} diff --git a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html new file mode 100644 index 00000000..067d3ebb --- /dev/null +++ b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.html @@ -0,0 +1,125 @@ +

+ {{'admin.settings.header' | translate}} +

+
+
+

+ {{'admin.settings.website.header' | translate}} +

+
+ +
+
+
+
{{'admin.settings.website.frontend_version' | translate}}:
+
{{data.webVersion}}
+
+
+ +
+
+
{{'admin.settings.website.backend_version' | translate}}:
+
{{data.apiVersion}}
+
+
+ +
+
+
{{'admin.settings.website.config_path' | translate}}:
+
{{data.configPath}}
+
+
+ +
+
+
{{'admin.settings.website.frontend_base_url' | translate}}:
+
{{data.webBaseURL}}
+
+
+ +
+
+
{{'admin.settings.website.backend_base_url' | translate}}:
+
{{data.apiBaseURL}}
+
+
+ +
+ +
+
+
{{'admin.settings.website.token_expire_time' | translate}}:
+
{{data.tokenExpireTime}} {{'general.minutes' | translate}}
+
+
+ +
+
+
{{'admin.settings.website.refresh_token_expire_time' | translate}}:
+
{{data.refreshTokenExpireTime}} {{'general.days' | translate}}
+
+
+
+
+ +
+
+

+ {{'admin.settings.e_mail.header' | translate}} +

+
+ +
+
+
+
{{'admin.settings.e_mail.user' | translate}}:
+
{{data.mailUser}}
+
+
+ +
+
+
{{'admin.settings.e_mail.host' | translate}}:
+
{{data.mailHost}}
+
+
+ +
+
+
{{'admin.settings.e_mail.port' | translate}}:
+
{{data.mailPort}}
+
+
+ +
+
+
{{'admin.settings.e_mail.transceiver' | translate}}:
+
{{data.mailTransceiver}}
+
+
+ +
+
+
{{'admin.settings.e_mail.e_mail_address' | translate}}:
+
{{data.mailTransceiverAddress}}
+
+
+ +
+
+
+
+ +
+
+ +
+ +
+
+
+
+
\ No newline at end of file diff --git a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.scss b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.spec.ts b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.spec.ts new file mode 100644 index 00000000..a3a508b0 --- /dev/null +++ b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SettingsComponent } from './settings.component'; + +describe('SettingsComponent', () => { + let component: SettingsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ SettingsComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SettingsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.ts b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.ts new file mode 100644 index 00000000..020a8f0e --- /dev/null +++ b/kdb-web/src/app/modules/admin/settings/components/settings/settings.component.ts @@ -0,0 +1,121 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { catchError } from 'rxjs/operators'; +import { SettingsDTO } from 'src/app/models/config/settings.dto'; +import { ErrorDTO } from 'src/app/models/error/error-dto'; +import { ServiceErrorCode } from 'src/app/models/error/service-error-code.enum'; +import { AuthService } from 'src/app/services/auth/auth.service'; +import { GuiService } from 'src/app/services/gui/gui.service'; +import { SettingsService } from 'src/app/services/settings/settings.service'; +import { SpinnerService } from 'src/app/services/spinner/spinner.service'; +import { ToastService } from 'src/app/services/toast/toast.service'; + +@Component({ + selector: 'app-settings', + templateUrl: './settings.component.html', + styleUrls: ['./settings.component.scss'] +}) +export class SettingsComponent implements OnInit { + + testMailForm!: FormGroup; + data: SettingsDTO = { + webVersion: '', + apiVersion: '', + configPath: '', + webBaseURL: '', + apiBaseURL: '', + + tokenExpireTime: 0, + refreshTokenExpireTime: 0, + + mailUser: '', + mailPort: 0, + mailHost: '', + mailTransceiver: '', + mailTransceiverAddress: '', + }; + + constructor( + private settingsService: SettingsService, + private spinnerService: SpinnerService, + private guiService: GuiService, + private formBuilder: FormBuilder, + private toastService: ToastService, + private translate: TranslateService + ) { } + + ngOnInit(): void { + this.spinnerService.showSpinner(); + this.initForms(); + + this.guiService.getSettings() + .pipe(catchError(err => { + this.spinnerService.hideSpinner(); + throw err; + })) + .subscribe(settings => { + this.spinnerService.hideSpinner(); + this.data = settings; + this.data.webVersion = this.settingsService.getWebVersion()?.getVersionString() ?? '0.0.0'; + this.data.apiBaseURL = this.settingsService.getApiURL(); + if (!this.data.apiBaseURL.endsWith('/')) { + this.data.apiBaseURL += '/'; + } + }); + } + + initForms(): void { + this.testMailForm = this.formBuilder.group({ + mail: [null, [Validators.required, Validators.email]], + }); + } + + testMail(): void { + this.spinnerService.showSpinner(); + + const mail = this.testMailForm.value.mail; + if (!mail) { + this.spinnerService.hideSpinner(); + return; + } + + this.guiService.sendTestMail(mail) + .pipe(catchError(error => { + let header = this.translate.instant('admin.settings.message.error'); + let message = this.translate.instant('admin.settings.message.could_not_send_mail'); + + if (error.error !== null) { + const err: ErrorDTO = error.error; + + if (err.errorCode === ServiceErrorCode.ConnectionFailed) { + header = this.translate.instant('admin.settings.message.connection_failed'); + message = this.translate.instant('admin.settings.message.connection_to_mail_failed'); + error.error = null; + } + + if (err.errorCode === ServiceErrorCode.InvalidUser) { + header = this.translate.instant('admin.settings.message.connection_failed'); + message = this.translate.instant('admin.settings.message.mail_login_failed'); + error.error = null; + } + + if (err.errorCode === ServiceErrorCode.MailError) { + header = this.translate.instant('admin.settings.message.send_failed'); + message = this.translate.instant('admin.settings.message.test_mail_not_send'); + error.error = null; + } + } + + this.spinnerService.hideSpinner(); + this.toastService.error(header, message); + throw error; + })) + .subscribe(res => { + this.spinnerService.hideSpinner(); + this.toastService.success(this.translate.instant('admin.settings.message.success'), this.translate.instant('admin.settings.message.send_mail')); + this.testMailForm.reset(); + }); + } + +} diff --git a/kdb-web/src/app/modules/admin/settings/settings-routing.module.ts b/kdb-web/src/app/modules/admin/settings/settings-routing.module.ts new file mode 100644 index 00000000..12585a86 --- /dev/null +++ b/kdb-web/src/app/modules/admin/settings/settings-routing.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { SettingsComponent } from './components/settings/settings.component'; + +const routes: Routes = [ + {path:'', component: SettingsComponent} +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class SettingsRoutingModule { } diff --git a/kdb-web/src/app/modules/admin/settings/settings.module.ts b/kdb-web/src/app/modules/admin/settings/settings.module.ts new file mode 100644 index 00000000..3eae8106 --- /dev/null +++ b/kdb-web/src/app/modules/admin/settings/settings.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { SettingsRoutingModule } from './settings-routing.module'; +import { SettingsComponent } from './components/settings/settings.component'; +import { SharedModule } from '../../shared/shared.module'; + + +@NgModule({ + declarations: [ + SettingsComponent + ], + imports: [ + CommonModule, + SettingsRoutingModule, + SharedModule + ] +}) +export class SettingsModule { } diff --git a/kdb-web/src/app/modules/auth/auth-routing.module.ts b/kdb-web/src/app/modules/auth/auth-routing.module.ts new file mode 100644 index 00000000..d19b6a52 --- /dev/null +++ b/kdb-web/src/app/modules/auth/auth-routing.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { ForgetPasswordComponent } from './components/forget-password/forget-password.component'; +import { LoginComponent } from './components/login/login.component'; +import { RegistrationComponent } from './components/registration/registration.component'; + +const routes: Routes = [ + { path: 'login', component: LoginComponent }, + { path: 'register', component: RegistrationComponent }, + { path: 'register/:id', component: RegistrationComponent }, + { path: 'forgot-password', component: ForgetPasswordComponent }, + { path: 'forgot-password/:id', component: ForgetPasswordComponent }, + { path: 'forgot-password', component: ForgetPasswordComponent }, + { path: 'forgot-password/:id', component: ForgetPasswordComponent }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class AuthRoutingModule { } diff --git a/kdb-web/src/app/modules/auth/auth.module.ts b/kdb-web/src/app/modules/auth/auth.module.ts new file mode 100644 index 00000000..4e7c48c3 --- /dev/null +++ b/kdb-web/src/app/modules/auth/auth.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { AuthRoutingModule } from './auth-routing.module'; +import { ForgetPasswordComponent } from './components/forget-password/forget-password.component'; +import { LoginComponent } from './components/login/login.component'; +import { RegistrationComponent } from './components/registration/registration.component'; +import { SharedModule } from '../shared/shared.module'; + + +@NgModule({ + declarations: [ + ForgetPasswordComponent, + LoginComponent, + RegistrationComponent + ], + imports: [ + CommonModule, + AuthRoutingModule, + SharedModule + ] +}) +export class AuthModule { } diff --git a/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.html b/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.html new file mode 100644 index 00000000..e1fee63e --- /dev/null +++ b/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.html @@ -0,0 +1,57 @@ + \ No newline at end of file diff --git a/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.scss b/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.spec.ts b/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.spec.ts new file mode 100644 index 00000000..701620a9 --- /dev/null +++ b/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ForgetPasswordComponent } from './forget-password.component'; + +describe('ForgetPasswordComponent', () => { + let component: ForgetPasswordComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ForgetPasswordComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ForgetPasswordComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.ts b/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.ts new file mode 100644 index 00000000..c49c6be1 --- /dev/null +++ b/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.ts @@ -0,0 +1,136 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { catchError } from 'rxjs/operators'; +import { ResetPasswordDTO } from 'src/app/models/auth/reset-password.dto'; +import { AuthService } from 'src/app/services/auth/auth.service'; +import { SpinnerService } from 'src/app/services/spinner/spinner.service'; +import { ToastService } from 'src/app/services/toast/toast.service'; + +@Component({ + selector: 'app-forget-password', + templateUrl: './forget-password.component.html', + styleUrls: ['./forget-password.component.scss'] +}) +export class ForgetPasswordComponent implements OnInit { + + emailForm!: FormGroup; + passwordForm!: FormGroup; + submitted = false; + ready = false; + repeatErrors = { + email: false, + password: false + }; + + resetPasswordId: string | null = null; + + constructor( + private authService: AuthService, + private formBuilder: FormBuilder, + private router: Router, + private spinnerService: SpinnerService, + private route: ActivatedRoute, + private toastService: ToastService, + private translate: TranslateService + ) { } + + ngOnInit(): void { + this.spinnerService.showSpinner(); + this.authService.isUserLoggedInAsync().then(result => { + if (result) { + this.router.navigate(['/dashboard']); + } + + this.initForms(); + this.checkResetPasswordId(); + this.spinnerService.hideSpinner(); + }); + } + + initForms(): void { + this.emailForm = this.formBuilder.group({ + email: [null, [Validators.required, Validators.email]] + }); + + this.passwordForm = this.formBuilder.group({ + password: [null, [Validators.required, Validators.minLength(8)]], + passwordRepeat: [null, [Validators.required, Validators.minLength(8)]] + }); + } + + login(): void { + this.router.navigate(['/auth/login']); + } + + register(): void { + this.router.navigate(['/auth/register']); + } + + forgotPassword(): void { + this.submitted = true; + + if (this.emailForm.invalid) { + return; + } + + this.spinnerService.showSpinner(); + this.authService.forgotPassword(this.emailForm.value.email) + .pipe(catchError(err => { + this.spinnerService.hideSpinner(); + throw err; + })).subscribe(res => { + this.spinnerService.hideSpinner(); + this.ready = true; + setTimeout(() => { this.router.navigate(['/dashboard']); }, 5000); + }); + } + + checkResetPasswordId(): void { + const id = this.route.snapshot.params['id']; + if (id) { + this.resetPasswordId = id; + this.spinnerService.showSpinner(); + this.authService.getEMailFromforgotPasswordId(id) + .pipe(catchError(err => { + this.spinnerService.hideSpinner(); + this.router.navigate(['/auth/forgot-password']); + throw err; + })).subscribe(email => { + this.spinnerService.hideSpinner(); + if (email) { + this.emailForm.value.email = email; + } else { + this.router.navigate(['/auth/forgot-password']); + } + }); + } + } + + resetPassword(): void { + const id = this.route.snapshot.params['id']; + if (this.emailForm.value.password !== this.emailForm.value.passwordRepeat) { + this.repeatErrors.password = true; + return; + } + this.spinnerService.showSpinner(); + + const resetPasswordDTO: ResetPasswordDTO = { + id, + password: this.passwordForm.value.password + }; + + this.authService.resetPassword(resetPasswordDTO) + .pipe(catchError(error => { + this.router.navigate(['/auth/login']); + this.spinnerService.hideSpinner(); + throw error; + })) + .subscribe(resp => { + this.spinnerService.hideSpinner(); + this.toastService.success(this.translate.instant('auth.forgot_password.message.reset_password'), this.translate.instant('auth.forgot_password.message.reset_password_d')); + this.router.navigate(['/auth/login']); + }); + } +} diff --git a/kdb-web/src/app/modules/auth/components/login/login.component.html b/kdb-web/src/app/modules/auth/components/login/login.component.html new file mode 100644 index 00000000..43ea33cd --- /dev/null +++ b/kdb-web/src/app/modules/auth/components/login/login.component.html @@ -0,0 +1,58 @@ + \ No newline at end of file diff --git a/kdb-web/src/app/modules/auth/components/login/login.component.scss b/kdb-web/src/app/modules/auth/components/login/login.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/auth/components/login/login.component.spec.ts b/kdb-web/src/app/modules/auth/components/login/login.component.spec.ts new file mode 100644 index 00000000..d2c0e6c8 --- /dev/null +++ b/kdb-web/src/app/modules/auth/components/login/login.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginComponent } from './login.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ LoginComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/auth/components/login/login.component.ts b/kdb-web/src/app/modules/auth/components/login/login.component.ts new file mode 100644 index 00000000..4a073c9a --- /dev/null +++ b/kdb-web/src/app/modules/auth/components/login/login.component.ts @@ -0,0 +1,114 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { AuthService } from 'src/app/services/auth/auth.service'; +import { AuthUserDTO } from 'src/app/models/auth/auth-user.dto'; +import { Router } from '@angular/router'; +import { catchError } from 'rxjs/operators'; +import { ErrorDTO } from 'src/app/models/error/error-dto'; +import { AuthErrorMessages } from 'src/app/models/auth/auth-error-messages.enum'; +import { ServiceErrorCode } from 'src/app/models/error/service-error-code.enum'; +import { AuthUserAtrErrors } from 'src/app/models/auth/auth-user-atr-errors'; +import { SpinnerService } from 'src/app/services/spinner/spinner.service'; +import { ThemeService } from 'src/app/services/theme/theme.service'; + +@Component({ + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.scss'] +}) +export class LoginComponent implements OnInit { + + loginForm!: FormGroup<{ + email: FormControl, + password: FormControl + }>; + submitted = false; + + authUserAtrErrors!: AuthUserAtrErrors; + + constructor( + private authService: AuthService, + private formBuilder: FormBuilder, + private router: Router, + private spinnerService: SpinnerService, + private themeService: ThemeService + ) { } + + ngOnInit(): void { + this.spinnerService.showSpinner(); + this.authService.isUserLoggedInAsync().then(result => { + if (result) { + this.router.navigate(['/dashboard']); + } + + this.initLoginForm(); + this.resetStateFlags(); + this.spinnerService.hideSpinner(); + }); + } + + resetStateFlags(): void { + this.authUserAtrErrors = new AuthUserAtrErrors(); + } + + initLoginForm(): void { + this.loginForm = this.formBuilder.group({ + email: ['', [Validators.required, Validators.email]], + password: ['', [Validators.required, Validators.minLength(8)]] + }); + } + + register(): void { + this.router.navigate(['/auth/register']); + } + + forgotPassword(): void { + this.router.navigate(['/auth/forgot-password']); + } + + login(): void { + this.submitted = true; + this.resetStateFlags(); + + if (this.loginForm.invalid) { + return; + } + + this.spinnerService.showSpinner(); + const user: AuthUserDTO = { + firstName: '', + lastName: '', + email: this.loginForm.value.email ?? null, + password: this.loginForm.value.password ?? null + }; + + this.authService.login(user) + .pipe(catchError(error => { + if (error.error !== null) { + const err: ErrorDTO = error.error; + + if (err.errorCode === ServiceErrorCode.InvalidData && err.message === AuthErrorMessages.UserIsEmpty) { + this.authUserAtrErrors.email.required = true; + this.authUserAtrErrors.password.required = true; + } else if (err.errorCode === ServiceErrorCode.InvalidUser && err.message === AuthErrorMessages.UserNotFound) { + this.authUserAtrErrors.email.wrongData = true; + } else if (err.errorCode === ServiceErrorCode.InvalidUser && err.message === AuthErrorMessages.WrongPassword) { + this.authUserAtrErrors.password.wrongData = true; + } else if (err.errorCode === ServiceErrorCode.InvalidUser && err.message === AuthErrorMessages.EMailNotConfirmed) { + this.authUserAtrErrors.email.notConfirmed = true; + } + error.error = null; + } + this.spinnerService.hideSpinner(); + throw error; + })) + .subscribe(token => { + this.authService.saveToken(token); + this.themeService.loadTheme(); + this.themeService.loadMenu(); + this.spinnerService.hideSpinner(); + this.router.navigate(['/dashboard']); + }); + } + +} diff --git a/kdb-web/src/app/modules/auth/components/registration/registration.component.html b/kdb-web/src/app/modules/auth/components/registration/registration.component.html new file mode 100644 index 00000000..2d9f41bd --- /dev/null +++ b/kdb-web/src/app/modules/auth/components/registration/registration.component.html @@ -0,0 +1,83 @@ + \ No newline at end of file diff --git a/kdb-web/src/app/modules/auth/components/registration/registration.component.scss b/kdb-web/src/app/modules/auth/components/registration/registration.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/auth/components/registration/registration.component.spec.ts b/kdb-web/src/app/modules/auth/components/registration/registration.component.spec.ts new file mode 100644 index 00000000..6c3a576f --- /dev/null +++ b/kdb-web/src/app/modules/auth/components/registration/registration.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegistrationComponent } from './registration.component'; + +describe('RegistrationComponent', () => { + let component: RegistrationComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ RegistrationComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RegistrationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/auth/components/registration/registration.component.ts b/kdb-web/src/app/modules/auth/components/registration/registration.component.ts new file mode 100644 index 00000000..3cd29c14 --- /dev/null +++ b/kdb-web/src/app/modules/auth/components/registration/registration.component.ts @@ -0,0 +1,144 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { catchError } from 'rxjs/operators'; +import { AuthErrorMessages } from 'src/app/models/auth/auth-error-messages.enum'; +import { AuthUserDTO } from 'src/app/models/auth/auth-user.dto'; +import { AuthUserAtrErrors } from 'src/app/models/auth/auth-user-atr-errors'; +import { ErrorDTO } from 'src/app/models/error/error-dto'; +import { ServiceErrorCode } from 'src/app/models/error/service-error-code.enum'; +import { AuthService } from 'src/app/services/auth/auth.service'; +import { SpinnerService } from 'src/app/services/spinner/spinner.service'; + +@Component({ + selector: 'app-registration', + templateUrl: './registration.component.html', + styleUrls: ['./registration.component.scss'] +}) +export class RegistrationComponent implements OnInit { + + loginForm!: FormGroup<{ + firstName: FormControl, + lastName: FormControl, + email: FormControl, + emailRepeat: FormControl, + password: FormControl, + passwordRepeat: FormControl + }>; + submitted = false; + authUserAtrErrors!: AuthUserAtrErrors; + repeatErrors = { + email: false, + password: false + }; + + showEMailConfirmation = false; + showEMailConfirmationError = false; + + constructor( + private authService: AuthService, + private formBuilder: FormBuilder, + private router: Router, + private spinnerService: SpinnerService, + private route: ActivatedRoute + ) { + this.spinnerService.showSpinner(); + this.authService.isUserLoggedInAsync().then(res => { + if (res) { + this.router.navigate(['/dashboard']); + } + this.spinnerService.hideSpinner(); + }); + } + + ngOnInit(): void { + this.initLoginForm(); + this.resetStateFlags(); + this.confirmEMail(); + } + + resetStateFlags(): void { + this.authUserAtrErrors = new AuthUserAtrErrors(); + this.repeatErrors = { + email: false, + password: false + }; + } + + login(): void { + this.router.navigate(['/auth/login']); + } + + initLoginForm(): void { + this.loginForm = this.formBuilder.group({ + firstName: ['', Validators.required], + lastName: ['', Validators.required], + email: ['', [Validators.required, Validators.email]], + emailRepeat: ['', [Validators.required, Validators.email]], + password: ['', [Validators.required, Validators.minLength(8)]], + passwordRepeat: ['', [Validators.required, Validators.minLength(8)]] + }); + } + + register(): void { + this.submitted = true; + this.resetStateFlags(); + + // stop here if form is invalid + if (this.loginForm.invalid) { + return; + } + + if (this.loginForm.value.email !== this.loginForm.value.emailRepeat) { + this.repeatErrors.email = true; + return; + } + + if (this.loginForm.value.password !== this.loginForm.value.passwordRepeat) { + this.repeatErrors.password = true; + return; + } + + this.spinnerService.showSpinner(); + const user: AuthUserDTO = { + firstName: this.loginForm.value.firstName ?? null, + lastName: this.loginForm.value.lastName ?? null, + email: this.loginForm.value.email ?? null, + password: this.loginForm.value.password ?? null + }; + + this.authService.register(user) + .pipe(catchError(error => { + if (error.error !== null) { + const err: ErrorDTO = error.error; + + if (err.errorCode === ServiceErrorCode.InvalidUser && err.message === AuthErrorMessages.UserAlreadyExists) { + this.authUserAtrErrors.email.wrongData = true; + } + } + this.spinnerService.hideSpinner(); + throw error; + })) + .subscribe(resp => { + this.spinnerService.hideSpinner(); + this.router.navigate(['/auth/login']); + }); + } + + confirmEMail(): void { + const id = this.route.snapshot.params['id']; + if (id) { + this.spinnerService.showSpinner(); + this.authService.confirmEMail(id) + .pipe(catchError(error => { + this.router.navigate(['/auth/login']); + this.spinnerService.hideSpinner(); + throw error; + })) + .subscribe(resp => { + this.spinnerService.hideSpinner(); + this.router.navigate(['/auth/login']); + }); + } + } +} diff --git a/kdb-web/src/app/modules/shared/guards/auth/auth.guard.spec.ts b/kdb-web/src/app/modules/shared/guards/auth/auth.guard.spec.ts new file mode 100644 index 00000000..68889d22 --- /dev/null +++ b/kdb-web/src/app/modules/shared/guards/auth/auth.guard.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AuthGuard } from './auth.guard'; + +describe('AuthGuard', () => { + let guard: AuthGuard; + + beforeEach(() => { + TestBed.configureTestingModule({}); + guard = TestBed.inject(AuthGuard); + }); + + it('should be created', () => { + expect(guard).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts b/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts new file mode 100644 index 00000000..694dc644 --- /dev/null +++ b/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts @@ -0,0 +1,38 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; +import { catchError } from 'rxjs/operators'; +import { AuthService } from 'src/app/services/auth/auth.service'; +import { ThemeService } from 'src/app/services/theme/theme.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthGuard implements CanActivate { + constructor( + private router: Router, + private authService: AuthService, + private themeService: ThemeService + ) { + } + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { + if (!this.authService.getToken().token) { + this.authService.logout(); + return false; + } + + if (!await this.authService.isUserLoggedInAsync()) { + this.authService.logout(); + return false; + } + + const role = route.data['role']; + if (role) { + if (!await this.authService.hasUserPermission(role)) { + this.router.navigate(['/dashboard']); + return false; + } + } + return true; + } +} diff --git a/kdb-web/src/app/modules/shared/pipes/auth-role.pipe.ts b/kdb-web/src/app/modules/shared/pipes/auth-role.pipe.ts new file mode 100644 index 00000000..47b0e092 --- /dev/null +++ b/kdb-web/src/app/modules/shared/pipes/auth-role.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { AuthRoles } from 'src/app/models/auth/auth-roles.enum'; + +@Pipe({ + name: 'authRole' +}) +export class AuthRolePipe implements PipeTransform { + + transform(value: AuthRoles): string { + return AuthRoles[value].toString(); + } + +} diff --git a/kdb-web/src/app/modules/shared/pipes/bool.pipe.spec.ts b/kdb-web/src/app/modules/shared/pipes/bool.pipe.spec.ts new file mode 100644 index 00000000..e78cbc01 --- /dev/null +++ b/kdb-web/src/app/modules/shared/pipes/bool.pipe.spec.ts @@ -0,0 +1,8 @@ +import { BoolPipe } from './bool.pipe'; + +describe('BoolPipe', () => { + it('create an instance', () => { + const pipe = new BoolPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/shared/pipes/bool.pipe.ts b/kdb-web/src/app/modules/shared/pipes/bool.pipe.ts new file mode 100644 index 00000000..fc2d54cf --- /dev/null +++ b/kdb-web/src/app/modules/shared/pipes/bool.pipe.ts @@ -0,0 +1,21 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; + +@Pipe({ + name: 'bool' +}) +export class BoolPipe implements PipeTransform { + + constructor( + private translate: TranslateService + ) {} + + transform(value: boolean): string { + if (value === true) { + return this.translate.instant('common.bool_as_string.true'); + } + + return this.translate.instant('common.bool_as_string.false'); + } + +} diff --git a/kdb-web/src/app/modules/shared/pipes/ip-address.pipe.ts b/kdb-web/src/app/modules/shared/pipes/ip-address.pipe.ts new file mode 100644 index 00000000..fb3f1176 --- /dev/null +++ b/kdb-web/src/app/modules/shared/pipes/ip-address.pipe.ts @@ -0,0 +1,27 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'ipAddress' +}) +export class IpAddressPipe implements PipeTransform { + + transform(ipAsArray: number[]): string { + let ipAsString = ""; + + if (ipAsArray.length != 4){ + throw new Error("Invalid IP") + } + + for (let i = 0; i < ipAsArray.length; i++) { + const byte = ipAsArray[i]; + if (i == ipAsArray.length - 1) { + ipAsString += `${byte}`; + } else { + ipAsString += `${byte}.`; + } + } + + return ipAsString; + } + +} diff --git a/kdb-web/src/app/modules/shared/shared.module.ts b/kdb-web/src/app/modules/shared/shared.module.ts new file mode 100644 index 00000000..9a77a513 --- /dev/null +++ b/kdb-web/src/app/modules/shared/shared.module.ts @@ -0,0 +1,71 @@ +import { CommonModule } from '@angular/common'; +import { HttpClientModule } from '@angular/common/http'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { TranslateModule } from '@ngx-translate/core'; +import { ButtonModule } from 'primeng/button'; +import { CheckboxModule } from 'primeng/checkbox'; +import { ConfirmDialogModule } from 'primeng/confirmdialog'; +import { DialogModule } from 'primeng/dialog'; +import { DropdownModule } from 'primeng/dropdown'; +import { DynamicDialogModule } from 'primeng/dynamicdialog'; +import { InputTextModule } from 'primeng/inputtext'; +import { MenuModule } from 'primeng/menu'; +import { PasswordModule } from 'primeng/password'; +import { ProgressSpinnerModule } from 'primeng/progressspinner'; +import { TableModule } from 'primeng/table'; +import { ToastModule } from 'primeng/toast'; +import { AuthRolePipe } from './pipes/auth-role.pipe'; +import { IpAddressPipe } from './pipes/ip-address.pipe'; +import { BoolPipe } from './pipes/bool.pipe'; + + + +@NgModule({ + declarations: [ + AuthRolePipe, + IpAddressPipe, + BoolPipe, + ], + imports: [ + CommonModule, + ButtonModule, + PasswordModule, + MenuModule, + DialogModule, + ProgressSpinnerModule, + HttpClientModule, + FormsModule, + ReactiveFormsModule, + ToastModule, + ConfirmDialogModule, + TableModule, + InputTextModule, + CheckboxModule, + DropdownModule, + TranslateModule, + DynamicDialogModule + ], + exports: [ + ButtonModule, + PasswordModule, + MenuModule, + DialogModule, + ProgressSpinnerModule, + HttpClientModule, + FormsModule, + ReactiveFormsModule, + ToastModule, + ConfirmDialogModule, + TableModule, + InputTextModule, + CheckboxModule, + DropdownModule, + TranslateModule, + DynamicDialogModule, + AuthRolePipe, + IpAddressPipe, + BoolPipe, + ] +}) +export class SharedModule { } diff --git a/kdb-web/src/app/modules/view/change-password/change-password-routing.module.ts b/kdb-web/src/app/modules/view/change-password/change-password-routing.module.ts new file mode 100644 index 00000000..59da6844 --- /dev/null +++ b/kdb-web/src/app/modules/view/change-password/change-password-routing.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { ChangePasswordComponent } from './components/change-password/change-password.component'; + +const routes: Routes = [ + {path: '', component:ChangePasswordComponent} +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ChangePasswordRoutingModule { } diff --git a/kdb-web/src/app/modules/view/change-password/change-password.module.ts b/kdb-web/src/app/modules/view/change-password/change-password.module.ts new file mode 100644 index 00000000..05d47342 --- /dev/null +++ b/kdb-web/src/app/modules/view/change-password/change-password.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { ChangePasswordRoutingModule } from './change-password-routing.module'; +import { ChangePasswordComponent } from './components/change-password/change-password.component'; +import { SharedModule } from '../../shared/shared.module'; + + +@NgModule({ + declarations: [ + ChangePasswordComponent + ], + imports: [ + CommonModule, + ChangePasswordRoutingModule, + SharedModule + ] +}) +export class ChangePasswordModule { } diff --git a/kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.html b/kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.html new file mode 100644 index 00000000..0532651f --- /dev/null +++ b/kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.html @@ -0,0 +1,35 @@ +

+ {{'view.change-password.header' | translate}} +

+
+
+
+
+ +
+
{{'view.change-password.wrong_password' | translate}}
+
+
+ +
+ +
+ +
+ +
+
{{'view.change-password.passwords_do_not_match' | translate}}
+
+
+ + +
+
+
\ No newline at end of file diff --git a/kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.scss b/kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.spec.ts b/kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.spec.ts new file mode 100644 index 00000000..0776caea --- /dev/null +++ b/kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChangePasswordComponent } from './change-password.component'; + +describe('ChangePasswordComponent', () => { + let component: ChangePasswordComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ChangePasswordComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ChangePasswordComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.ts b/kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.ts new file mode 100644 index 00000000..29fb7f2f --- /dev/null +++ b/kdb-web/src/app/modules/view/change-password/components/change-password/change-password.component.ts @@ -0,0 +1,107 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { catchError } from 'rxjs/operators'; +import { AuthErrorMessages } from 'src/app/models/auth/auth-error-messages.enum'; +import { UpdateUserDTO } from 'src/app/models/auth/update-user.dto'; +import { ErrorDTO } from 'src/app/models/error/error-dto'; +import { ServiceErrorCode } from 'src/app/models/error/service-error-code.enum'; +import { AuthService } from 'src/app/services/auth/auth.service'; +import { SpinnerService } from 'src/app/services/spinner/spinner.service'; +import { ToastService } from 'src/app/services/toast/toast.service'; + +@Component({ + selector: 'app-change-password', + templateUrl: './change-password.component.html', + styleUrls: ['./change-password.component.scss'] +}) +export class ChangePasswordComponent implements OnInit { + + passwordForm!: FormGroup; + submitted = false; + ready = false; + errors = { + email: false, + repeatPassword: false, + oldPassword: false + }; + + constructor( + private authService: AuthService, + private formBuilder: FormBuilder, + private router: Router, + private spinnerService: SpinnerService, + private toastService: ToastService, + private translate: TranslateService + ) { } + + ngOnInit(): void { + this.initForms(); + } + + initForms(): void { + this.passwordForm = this.formBuilder.group({ + oldPassword: [null, [Validators.required, Validators.minLength(8)]], + password: [null, [Validators.required, Validators.minLength(8)]], + passwordRepeat: [null, [Validators.required, Validators.minLength(8)]] + }); + } + + resetErrorFlags(): void { + this.errors = { + email: false, + repeatPassword: false, + oldPassword: false + }; + } + + changePassword(): void { + this.submitted = true; + this.resetErrorFlags(); + + if (this.passwordForm.value.password !== this.passwordForm.value.passwordRepeat) { + this.errors.repeatPassword = true; + return; + } + this.spinnerService.showSpinner(); + + const decodedToken = this.authService.getDecodedToken(); + const changePasswordDTO: UpdateUserDTO = { + authUserDTO: { + firstName: null, + lastName: null, + password: this.passwordForm.value.oldPassword, + email: this.authService.getEMailFromDecodedToken(decodedToken) + }, + newAuthUserDTO: { + firstName: null, + lastName: null, + password: this.passwordForm.value.password, + email: this.authService.getEMailFromDecodedToken(decodedToken) + } + }; + + this.authService.updateUser(changePasswordDTO) + .pipe(catchError(error => { + if (error.error !== null) { + const err: ErrorDTO = error.error; + + if (err.errorCode === ServiceErrorCode.InvalidUser && err.message === AuthErrorMessages.WrongPassword) { + this.errors.oldPassword = true; + } + + error.error = null; + } + this.spinnerService.hideSpinner(); + this.toastService.error(this.translate.instant('view.change_password.message.error'), this.translate.instant('view.change_password.message.password_cannot_be_changed')); + throw error; + })) + .subscribe(resp => { + this.spinnerService.hideSpinner(); + this.toastService.success(this.translate.instant('view.change_password.message.change_password'), this.translate.instant('view.change_password.message.changed_password')); + this.router.navigate(["/dashboard"]); + }); + } + +} diff --git a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html new file mode 100644 index 00000000..04e795ec --- /dev/null +++ b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html @@ -0,0 +1,3 @@ +

dashboard works!

+ +
diff --git a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.scss b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.spec.ts b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.spec.ts new file mode 100644 index 00000000..5ec4ff8f --- /dev/null +++ b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DashboardComponent } from './dashboard.component'; + +describe('DashboardComponent', () => { + let component: DashboardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DashboardComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DashboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts new file mode 100644 index 00000000..25d48a7a --- /dev/null +++ b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts @@ -0,0 +1,14 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-dashboard', + templateUrl: './dashboard.component.html', + styleUrls: ['./dashboard.component.scss'] +}) +export class DashboardComponent implements OnInit { + + constructor() { } + + ngOnInit(): void {} + +} diff --git a/kdb-web/src/app/modules/view/dashboard/dashboard-routing.module.ts b/kdb-web/src/app/modules/view/dashboard/dashboard-routing.module.ts new file mode 100644 index 00000000..27f77246 --- /dev/null +++ b/kdb-web/src/app/modules/view/dashboard/dashboard-routing.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { DashboardComponent } from './components/dashboard/dashboard.component'; + +const routes: Routes = [ + {path: '', component: DashboardComponent} +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class DashboardRoutingModule { } diff --git a/kdb-web/src/app/modules/view/dashboard/dashboard.module.ts b/kdb-web/src/app/modules/view/dashboard/dashboard.module.ts new file mode 100644 index 00000000..0b3abe11 --- /dev/null +++ b/kdb-web/src/app/modules/view/dashboard/dashboard.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { DashboardRoutingModule } from './dashboard-routing.module'; +import { DashboardComponent } from './components/dashboard/dashboard.component'; +import { SharedModule } from '../../shared/shared.module'; + + +@NgModule({ + declarations: [ + DashboardComponent + ], + imports: [ + CommonModule, + DashboardRoutingModule, + SharedModule + ] +}) +export class DashboardModule { } diff --git a/kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.html b/kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.html new file mode 100644 index 00000000..716df73c --- /dev/null +++ b/kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.html @@ -0,0 +1,41 @@ +

+ {{'view.user_settings.header' | translate}} +

+
+
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+
{{'view.user_settings.e_mail_already_exists' | translate}}
+
+
+ +
+ +
+
{{'view.user_settings.wrong_password' | translate}}
+
+
+ + +
+
+
\ No newline at end of file diff --git a/kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.scss b/kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.spec.ts b/kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.spec.ts new file mode 100644 index 00000000..4f529481 --- /dev/null +++ b/kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UserSettingsComponent } from './user-settings.component'; + +describe('UserSettingsComponent', () => { + let component: UserSettingsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ UserSettingsComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(UserSettingsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.ts b/kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.ts new file mode 100644 index 00000000..b4e8d633 --- /dev/null +++ b/kdb-web/src/app/modules/view/user-settings/components/user-settings/user-settings.component.ts @@ -0,0 +1,151 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { catchError } from 'rxjs/operators'; +import { AuthErrorMessages } from 'src/app/models/auth/auth-error-messages.enum'; +import { AuthUserDTO } from 'src/app/models/auth/auth-user.dto'; +import { UpdateUserDTO } from 'src/app/models/auth/update-user.dto'; +import { ErrorDTO } from 'src/app/models/error/error-dto'; +import { ServiceErrorCode } from 'src/app/models/error/service-error-code.enum'; +import { AuthService } from 'src/app/services/auth/auth.service'; +import { SpinnerService } from 'src/app/services/spinner/spinner.service'; +import { ThemeService } from 'src/app/services/theme/theme.service'; +import { ToastService } from 'src/app/services/toast/toast.service'; + +@Component({ + selector: 'app-user-settings', + templateUrl: './user-settings.component.html', + styleUrls: ['./user-settings.component.scss'] +}) +export class UserSettingsComponent implements OnInit { + settingsForm!: FormGroup<{ + firstName: FormControl, + lastName: FormControl, + email: FormControl, + password: FormControl + }>; + submitted = false; + ready = false; + errors = { + email: false, + password: false + }; + + authUser: AuthUserDTO | null = null; + + constructor( + private authService: AuthService, + private formBuilder: FormBuilder, + private router: Router, + private spinnerService: SpinnerService, + private toastService: ToastService, + private themeService: ThemeService, + private translaste: TranslateService + ) { } + + ngOnInit(): void { + this.initForms(); + this.load(); + } + + initForms(): void { + this.settingsForm = this.formBuilder.group({ + firstName: ['', [Validators.required]], + lastName: ['', [Validators.required]], + email: ['', [Validators.required, Validators.email]], + password: ['', [Validators.required, Validators.minLength(8)]] + }); + } + + resetErrorFlags(): void { + this.errors = { + email: false, + password: false + }; + } + + load(): void { + const token = this.authService.getDecodedToken(); + const mail = this.authService.getEMailFromDecodedToken(token); + if (!mail) { + return; + } + + this.authService.findUserByEMail(mail) + .pipe(catchError(err => { + this.toastService.error(this.translaste.instant('view.user_settings.message.user_not_found'), this.translaste.instant('view.user_settings.message.user_not_found_d')); + this.authService.logout(); + throw err; + })) + .subscribe(user => { + if (!user) { + this.toastService.error(this.translaste.instant('view.user_settings.message.user_not_found'), this.translaste.instant('view.user_settings.message.user_not_found_d')); + this.authService.logout(); + } + this.authUser = user; + this.settingsForm.controls.firstName.setValue(user.firstName); + this.settingsForm.controls.lastName.setValue(user.lastName); + this.settingsForm.controls.email.setValue(user.email); + }); + } + + saveSettings(): void { + if (!this.authUser) { + return; + } + + this.resetErrorFlags(); + this.submitted = true; + this.spinnerService.showSpinner(); + + this.authUser.password = this.settingsForm.value.password ?? null; + const updateUserDTO: UpdateUserDTO = { + authUserDTO: this.authUser, + newAuthUserDTO: { + firstName: this.settingsForm.value.firstName ?? null, + lastName: this.settingsForm.value.lastName ?? null, + email: this.settingsForm.value.email ?? null, + password: null + } + }; + + this.authService.updateUser(updateUserDTO) + .pipe(catchError(error => { + if (error.error !== null) { + const err: ErrorDTO = error.error; + + if (err.errorCode === ServiceErrorCode.InvalidUser && err.message === AuthErrorMessages.WrongPassword) { + this.errors.password = true; + } else if (err.errorCode === ServiceErrorCode.InvalidUser && err.message === AuthErrorMessages.UserAlreadyExists) { + this.errors.email = true; + } + } + this.spinnerService.hideSpinner(); + this.toastService.error(this.translaste.instant('view.user_settings.message.error'), this.translaste.instant('view.user_settings.message.could_not_change_settings')); + throw error; + })) + .subscribe(resp => { + updateUserDTO.newAuthUserDTO.password = updateUserDTO.authUserDTO.password; + this.authService.login(updateUserDTO.newAuthUserDTO) + .pipe(catchError(err => { + this.router.navigate(['/auth/login']); + throw err; + })) + .subscribe(token => { + this.spinnerService.hideSpinner(); + if (token) { + this.toastService.success(this.translaste.instant('view.user_settings.message.success'), this.translaste.instant('view.user_settings.message.changed_settings')); + this.authService.saveToken(token); + this.themeService.loadTheme(); + this.themeService.loadMenu(); + this.load(); + return true; + } + this.router.navigate(['/auth/login']); + return false; + }); + }); + } + +} diff --git a/kdb-web/src/app/modules/view/user-settings/user-settings-routing.module.ts b/kdb-web/src/app/modules/view/user-settings/user-settings-routing.module.ts new file mode 100644 index 00000000..adbc0cd0 --- /dev/null +++ b/kdb-web/src/app/modules/view/user-settings/user-settings-routing.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { UserSettingsComponent } from './components/user-settings/user-settings.component'; + +const routes: Routes = [ + { path: '', component: UserSettingsComponent } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class UserSettingsRoutingModule { } diff --git a/kdb-web/src/app/modules/view/user-settings/user-settings.module.ts b/kdb-web/src/app/modules/view/user-settings/user-settings.module.ts new file mode 100644 index 00000000..722555da --- /dev/null +++ b/kdb-web/src/app/modules/view/user-settings/user-settings.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { UserSettingsRoutingModule } from './user-settings-routing.module'; +import { UserSettingsComponent } from './components/user-settings/user-settings.component'; +import { SharedModule } from '../../shared/shared.module'; + + +@NgModule({ + declarations: [ + UserSettingsComponent + ], + imports: [ + CommonModule, + UserSettingsRoutingModule, + SharedModule + ] +}) +export class UserSettingsModule { } diff --git a/kdb-web/src/app/services/auth/auth.service.spec.ts b/kdb-web/src/app/services/auth/auth.service.spec.ts new file mode 100644 index 00000000..f1251cac --- /dev/null +++ b/kdb-web/src/app/services/auth/auth.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AuthService } from './auth.service'; + +describe('AuthService', () => { + let service: AuthService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AuthService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/services/auth/auth.service.ts b/kdb-web/src/app/services/auth/auth.service.ts new file mode 100644 index 00000000..f388e291 --- /dev/null +++ b/kdb-web/src/app/services/auth/auth.service.ts @@ -0,0 +1,243 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { JwtHelperService } from '@auth0/angular-jwt'; +import { Observable, Subscription } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { AdminUpdateUserDTO } from 'src/app/models/auth/admin-update-user.dto'; +import { AuthRoles } from 'src/app/models/auth/auth-roles.enum'; +import { AuthUserDTO } from 'src/app/models/auth/auth-user.dto'; +import { EMailStringDTO } from 'src/app/models/auth/email-string.dto'; +import { ResetPasswordDTO } from 'src/app/models/auth/reset-password.dto'; +import { TokenDTO } from 'src/app/models/auth/token.dto'; +import { UpdateUserDTO } from 'src/app/models/auth/update-user.dto'; +import { AuthUserSelectCriterion } from 'src/app/models/selection/auth-user/auth-user-select-criterion.dto'; +import { GetFilteredAuthUsersResultDTO } from 'src/app/models/selection/auth-user/get-filtered-auth-users-result.dto'; +import { SettingsService } from '../settings/settings.service'; +import { SpinnerService } from '../spinner/spinner.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + + invalidLogin!: boolean; + isLoggedIn!: boolean; + + constructor( + private appsettings: SettingsService, + private http: HttpClient, + private router: Router, + private jwtHelper: JwtHelperService, + private spinnerService: SpinnerService + ) { + this.isUserLoggedInAsync(); + } + + /* data requests */ + getAllUsers(): Observable> { + return this.http.get>(`${this.appsettings.getApiURL()}/api/auth/users`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + getFilteredUsers(selectCriterions: AuthUserSelectCriterion): Observable { + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/users/get/filtered`, selectCriterions, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + getUserByEMail(email: string): Observable { + return this.http.get(`${this.appsettings.getApiURL()}/api/auth/users/get/${email}`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + findUserByEMail(email: string): Observable { + return this.http.get(`${this.appsettings.getApiURL()}/api/auth/users/find/${email}`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + /* auth requsts */ + register(user: AuthUserDTO): Observable { + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/register`, user, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + confirmEMail(id: string): Observable { + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/register/${id}`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + login(user: AuthUserDTO): Observable { + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/login`, user, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + forgotPassword(email: string): Observable { + const emailJson = JSON.stringify(email); + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/forgot-password`, emailJson, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + getEMailFromforgotPasswordId(id: string): Observable { + const idJson = JSON.stringify(id); + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/confirm-forgot-password`, idJson, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + resetPassword(resetPasswordDTO: ResetPasswordDTO): Observable { + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/reset-password`, resetPasswordDTO, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + updateUser(updateUserDTO: UpdateUserDTO): Observable { + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/update-user`, updateUserDTO, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + updateUserAsAdmin(updateUserDTO: AdminUpdateUserDTO): Observable { + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/update-user-as-admin`, updateUserDTO, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + refresh(token: TokenDTO): Observable { + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/refresh`, token, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + logout(): Subscription | null { + const token = this.getToken(); + this.isLoggedIn = false; + localStorage.removeItem('jwt'); + localStorage.removeItem('rjwt'); + this.router.navigate(['/auth/login']); + + if (token && token.token && token.refreshToken) { + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/revoke`, token, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }).pipe(catchError((error: any) => { + error.error = null; + throw error; + })).subscribe(); + } + + return null + } + + deleteUserByMail(mail: string) { + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/delete-user-by-mail/${mail}`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + /* utils */ + saveToken(token: TokenDTO): void { + localStorage.setItem('jwt', token.token); + localStorage.setItem('rjwt', token.refreshToken); + if (this.router.url.startsWith('/auth')) { + this.router.navigate(['/dashboard']); + } + } + + getToken(): TokenDTO { + return { + token: localStorage.getItem('jwt') ?? '', + refreshToken: localStorage.getItem('rjwt') ?? '' + }; + } + + getDecodedToken(): { [key: string]: any } { + return this.jwtHelper.decodeToken(this.getToken().token); + } + + async isUserLoggedInAsync(): Promise { + this.isLoggedIn = await this._isUserLoggedInAsync(); + return this.isLoggedIn; + } + + private async _isUserLoggedInAsync(): Promise { + const token = this.getToken(); + if (!token || !token.refreshToken) { + this.isLoggedIn = false; + return false; + } + + if (token.token && !this.jwtHelper.isTokenExpired(token.token)) { + this.isLoggedIn = true; + return true; + } + + if (this.isLoggedIn !== false) { + this.spinnerService.showSpinner(); + const resfreshedToken = await this.refresh(token) + .pipe(catchError((err: Error) => { + this.logout(); + this.spinnerService.hideSpinner(); + throw err; + })) + .toPromise(); + this.spinnerService.hideSpinner(); + if (resfreshedToken) { + this.saveToken(resfreshedToken); + return true; + } + } + this.isLoggedIn = false; + return false; + } + + async hasUserPermission(role: AuthRoles): Promise { + if (!role || !await this.isUserLoggedInAsync()) { + return false; + } + const token = this.getDecodedToken(); + return AuthRoles[token['role']] === AuthRoles[role]; + } + + getEMailFromDecodedToken(token: { [key: string]: any }): string | null { + if (!token) { + return null; + } + return token['email']; + } +} diff --git a/kdb-web/src/app/services/confirmation-dialog/confirmation-dialog.service.spec.ts b/kdb-web/src/app/services/confirmation-dialog/confirmation-dialog.service.spec.ts new file mode 100644 index 00000000..79f95ddb --- /dev/null +++ b/kdb-web/src/app/services/confirmation-dialog/confirmation-dialog.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ConfirmationDialogService } from './confirmation-dialog.service'; + +describe('ConfirmationDialogService', () => { + let service: ConfirmationDialogService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ConfirmationDialogService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/services/confirmation-dialog/confirmation-dialog.service.ts b/kdb-web/src/app/services/confirmation-dialog/confirmation-dialog.service.ts new file mode 100644 index 00000000..c50bb19c --- /dev/null +++ b/kdb-web/src/app/services/confirmation-dialog/confirmation-dialog.service.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@angular/core'; +import { ConfirmationService } from 'primeng/api'; +import { ConfirmationDialog } from 'src/app/models/utils/confirmation-dialog'; + +@Injectable({ + providedIn: 'root' +}) +export class ConfirmationDialogService { + + constructor( + private confirmationService: ConfirmationService + ) { } + + errorDialog(header: string, message: string) { + this.confirmationService.confirm({ + key: 'errorConfirmationDialog', + header: header, + message: message + }); + } + + confirmDialog(header: string, message: string, accept?: () => void, reject?: () => void) { + let options: ConfirmationDialog = { + key: 'confirmConfirmationDialog', + header: header, + message: message + }; + + if (accept) { + options.accept = accept; + } + + if (reject) { + options.reject = reject; + } + + this.confirmationService.confirm(options); + } + + warningDialog(header: string, message: string, accept?: () => void) { + let options: ConfirmationDialog = { + key: 'warningConfirmationDialog', + header: header, + message: message + }; + + if (accept) { + options.accept = accept; + } + + this.confirmationService.confirm(options); + } +} diff --git a/kdb-web/src/app/services/data/data.service.spec.ts b/kdb-web/src/app/services/data/data.service.spec.ts new file mode 100644 index 00000000..38e8d9ec --- /dev/null +++ b/kdb-web/src/app/services/data/data.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { DataService } from './data.service'; + +describe('DataService', () => { + let service: DataService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(DataService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/services/data/data.service.ts b/kdb-web/src/app/services/data/data.service.ts new file mode 100644 index 00000000..36cccbb5 --- /dev/null +++ b/kdb-web/src/app/services/data/data.service.ts @@ -0,0 +1,14 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { SettingsService } from '../settings/settings.service'; + +@Injectable({ + providedIn: 'root' +}) +export class DataService { + + constructor( + private appsettings: SettingsService, + private http: HttpClient, + ) { } +} diff --git a/kdb-web/src/app/services/error-handler/error-handler.service.spec.ts b/kdb-web/src/app/services/error-handler/error-handler.service.spec.ts new file mode 100644 index 00000000..d74faff8 --- /dev/null +++ b/kdb-web/src/app/services/error-handler/error-handler.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ErrorHandlerService } from './error-handler.service'; + +describe('ErrorHandlerService', () => { + let service: ErrorHandlerService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ErrorHandlerService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/services/error-handler/error-handler.service.ts b/kdb-web/src/app/services/error-handler/error-handler.service.ts new file mode 100644 index 00000000..ac8bb59a --- /dev/null +++ b/kdb-web/src/app/services/error-handler/error-handler.service.ts @@ -0,0 +1,34 @@ +import { HttpErrorResponse } from '@angular/common/http'; +import { ErrorHandler, Injectable, Injector } from '@angular/core'; +import { Observable, throwError } from 'rxjs'; +import { ErrorDTO } from 'src/app/models/error/error-dto'; +import { ToastService } from '../toast/toast.service'; + +@Injectable() +export class ErrorHandlerService implements ErrorHandler { + + constructor( + private injector: Injector + ) { } + + handleError(error: HttpErrorResponse): Observable { + if (error && error.error) { + let message = 'Unbekannter Fehler'; + let header = 'Fehler'; + const errorDto: ErrorDTO = error.error; + + if (errorDto.errorCode !== undefined) { + header = 'Fehlercode: ' + errorDto.errorCode; + } + + if (errorDto.message) { + message = errorDto.message; + } else if (error.message) { + message = error.message; + } + + this.injector.get(ToastService).error(header, message); + } + return throwError(error); + } +} diff --git a/kdb-web/src/app/services/gui/gui.service.spec.ts b/kdb-web/src/app/services/gui/gui.service.spec.ts new file mode 100644 index 00000000..c4fc5035 --- /dev/null +++ b/kdb-web/src/app/services/gui/gui.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { GuiService } from './gui.service'; + +describe('GuiService', () => { + let service: GuiService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(GuiService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/services/gui/gui.service.ts b/kdb-web/src/app/services/gui/gui.service.ts new file mode 100644 index 00000000..20364e44 --- /dev/null +++ b/kdb-web/src/app/services/gui/gui.service.ts @@ -0,0 +1,41 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable, Subscribable } from 'rxjs'; +import { SettingsDTO } from 'src/app/models/config/settings.dto'; +import { SoftwareVersionDTO } from 'src/app/models/config/software-version.dto'; +import { SettingsService } from '../settings/settings.service'; + +@Injectable({ + providedIn: 'root' +}) +export class GuiService { + + constructor( + private appsettings: SettingsService, + private http: HttpClient, + ) { } + + getApiVersion(): Observable { + return this.http.get(`${this.appsettings.getApiURL()}/api/gui/api-version`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + getSettings(): Observable { + return this.http.get(`${this.appsettings.getApiURL()}/api/gui/settings`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + sendTestMail(mail: string): Observable { + return this.http.post(`${this.appsettings.getApiURL()}/api/gui/send-test-mail/${mail}`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } +} diff --git a/kdb-web/src/app/services/settings/settings.service.spec.ts b/kdb-web/src/app/services/settings/settings.service.spec.ts new file mode 100644 index 00000000..359cb6b7 --- /dev/null +++ b/kdb-web/src/app/services/settings/settings.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { SettingsService } from './settings.service'; + +describe('SettingsService', () => { + let service: SettingsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(SettingsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/services/settings/settings.service.ts b/kdb-web/src/app/services/settings/settings.service.ts new file mode 100644 index 00000000..17c292d1 --- /dev/null +++ b/kdb-web/src/app/services/settings/settings.service.ts @@ -0,0 +1,62 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { Appsettings } from 'src/app/models/config/appsettings'; +import { SoftwareVersion } from 'src/app/models/config/software-version'; +import { Theme } from 'src/app/models/view/theme'; +import { Themes } from 'src/app/models/view/themes.enum'; + +@Injectable({ + providedIn: 'root' +}) +export class SettingsService { + appsettings!: Appsettings; + + constructor( + private http: HttpClient + ) { } + + loadSettings(): Promise { + return new Promise((resolve, reject) => { + this.http.get('../../assets/config.json') + .pipe(catchError(error => { + reject(error); + return throwError(error); + })).subscribe(settings => { + this.appsettings = settings; + resolve(settings); + }); + }); + } + + public getApiURL(): string { + if (!this.appsettings || !this.appsettings.ApiURL) { + console.error('ApiURL is not set!'); + return ''; + } + return this.appsettings.ApiURL; + } + + public getWebVersion(): SoftwareVersion | null { + if (!this.appsettings || !this.appsettings.WebVersion) { + console.error('WebVersion is not set!'); + return null; + } + const webVersion = new SoftwareVersion( + this.appsettings.WebVersion.Major, + this.appsettings.WebVersion.Minor, + this.appsettings.WebVersion.Micro + ); + return webVersion; + } + + public getThemes(): Theme[] | null { + if (!this.appsettings || !this.appsettings.Themes) { + console.error('Themes is not set!'); + return null; + } + return this.appsettings.Themes; + } +} + diff --git a/kdb-web/src/app/services/spinner/spinner.service.spec.ts b/kdb-web/src/app/services/spinner/spinner.service.spec.ts new file mode 100644 index 00000000..222e634a --- /dev/null +++ b/kdb-web/src/app/services/spinner/spinner.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { SpinnerService } from './spinner.service'; + +describe('SpinnerService', () => { + let service: SpinnerService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(SpinnerService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/services/spinner/spinner.service.ts b/kdb-web/src/app/services/spinner/spinner.service.ts new file mode 100644 index 00000000..0d3aab97 --- /dev/null +++ b/kdb-web/src/app/services/spinner/spinner.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class SpinnerService { + + showSpinnerState = false; + constructor() { } + + showSpinner() { + this.showSpinnerState = true; + } + + hideSpinner() { + this.showSpinnerState = false; + } + + toggleSpinner() { + this.showSpinnerState = !this.showSpinnerState; + } +} diff --git a/kdb-web/src/app/services/theme/theme.service.spec.ts b/kdb-web/src/app/services/theme/theme.service.spec.ts new file mode 100644 index 00000000..1c2957ba --- /dev/null +++ b/kdb-web/src/app/services/theme/theme.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ThemeService } from './theme.service'; + +describe('ThemeService', () => { + let service: ThemeService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ThemeService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/services/theme/theme.service.ts b/kdb-web/src/app/services/theme/theme.service.ts new file mode 100644 index 00000000..47537679 --- /dev/null +++ b/kdb-web/src/app/services/theme/theme.service.ts @@ -0,0 +1,98 @@ +import { Injectable } from '@angular/core'; +import { Themes } from 'src/app/models/view/themes.enum'; +import { AuthService } from '../auth/auth.service'; + +@Injectable({ + providedIn: 'root' +}) +export class ThemeService { + + themeName: string | null; + + sidebarWidth = '150px'; + isSidebarOpen = false; + hasLangChanged = false; + + constructor( + private authService: AuthService + ) { + this.loadTheme(); + this.loadMenu(); + this.themeName = null; + } + + loadTheme(): void { + const token = this.authService.getDecodedToken(); + const mail = this.authService.getEMailFromDecodedToken(token); + + let defaultThemeName = localStorage.getItem(`default_themeName`); + if (!defaultThemeName || defaultThemeName != Themes.Default) { + defaultThemeName = Themes.Default; + } + + if (!mail) { + this.setTheme(defaultThemeName); + return; + } + + let userThemeName = localStorage.getItem(`${mail}_themeName`); + if (!userThemeName) { + this.setTheme(defaultThemeName); + return; + } + this.setTheme(userThemeName); + } + + setTheme(name: string): void { + this.authService.isUserLoggedInAsync().then(result => { + if (!result) { + localStorage.setItem(`default_themeName`, Themes.Default); + this.themeName = Themes.Default; + } + + const token = this.authService.getDecodedToken(); + const mail = this.authService.getEMailFromDecodedToken(token); + if (mail) { + localStorage.setItem(`${mail}_themeName`, name); + } + localStorage.setItem(`default_themeName`, name); + this.themeName = name; + }); + } + + loadMenu() { + const token = this.authService.getDecodedToken(); + const mail = this.authService.getEMailFromDecodedToken(token); + + if (!mail) { + this.setSideWidth(true); + return; + } + + let isMenuOpen = true; + let isMenuOpenStr = localStorage.getItem(`${mail}_isMenuOpen`); + if (isMenuOpenStr) { + isMenuOpen = Boolean(isMenuOpenStr); + } + + this.setIsMenuOpen(isMenuOpen); + } + + setIsMenuOpen(isMenuOpen: boolean) { + const token = this.authService.getDecodedToken(); + const mail = this.authService.getEMailFromDecodedToken(token); + + if (!mail) { + this.setSideWidth(true); + return; + } + + localStorage.setItem(`${mail}_isMenuOpen`, isMenuOpen + ""); + this.setSideWidth(isMenuOpen); + } + + setSideWidth($event: any): void { + this.sidebarWidth = $event ? '150px' : '50px'; + this.isSidebarOpen = $event; + } +} diff --git a/kdb-web/src/app/services/toast/toast.service.spec.ts b/kdb-web/src/app/services/toast/toast.service.spec.ts new file mode 100644 index 00000000..e0413db8 --- /dev/null +++ b/kdb-web/src/app/services/toast/toast.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ToastService } from './toast.service'; + +describe('ToastService', () => { + let service: ToastService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ToastService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/services/toast/toast.service.ts b/kdb-web/src/app/services/toast/toast.service.ts new file mode 100644 index 00000000..035a5579 --- /dev/null +++ b/kdb-web/src/app/services/toast/toast.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@angular/core'; +import { MessageService } from 'primeng/api'; +import { ToastOptions } from 'src/app/models/utils/toast-options'; + +@Injectable({ + providedIn: 'root' +}) +export class ToastService { + + constructor( + private messageService: MessageService + ) { } + + private toast(type: string, summary: string, detail: string, options?: ToastOptions) { + this.messageService.add({ + severity: type, + summary: summary, + detail: detail, + life: options?.life, + sticky: options?.sticky, + closable: options?.closable + }); + } + + success(summary: string, detail: string, options?: ToastOptions) { + this.toast('success', summary, detail, options); + } + + info(summary: string, detail: string, options?: ToastOptions) { + this.toast('info', summary, detail, options); + } + + warn(summary: string, detail: string, options?: ToastOptions) { + this.toast('warn', summary, detail, options); + } + + error(summary: string, detail: string, options?: ToastOptions) { + this.toast('error', summary, detail, options); + } +} diff --git a/kdb-web/src/assets/.gitkeep b/kdb-web/src/assets/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/assets/config.json b/kdb-web/src/assets/config.json new file mode 100644 index 00000000..905b2d2f --- /dev/null +++ b/kdb-web/src/assets/config.json @@ -0,0 +1,26 @@ +{ + "ApiURL": "http://localhost:5000", + "WebVersion": { + "Major": "0", + "Minor": "3", + "Micro": "0" + }, + "Themes": [ + { + "Label": "Blue light", + "Name": "default-light-theme" + }, + { + "Label": "Blue dark", + "Name": "default-dark-theme" + }, + { + "Label": "Orange light", + "Name": "sh-edraft-light-theme" + }, + { + "Label": "Orange dark", + "Name": "sh-edraft-dark-theme" + } + ] +} \ No newline at end of file diff --git a/kdb-web/src/assets/i18n/de.json b/kdb-web/src/assets/i18n/de.json new file mode 100644 index 00000000..b51660d4 --- /dev/null +++ b/kdb-web/src/assets/i18n/de.json @@ -0,0 +1,258 @@ +{ + "header": { + "change_password": "Passwort ändern", + "settings": "Einstellungen", + "logout": "Ausloggen", + "header": "Krümmelmonster WI" + }, + "sidebar": { + "dashboard": "Dashboard", + "domain_list": "Domänen", + "host_list": "Rechner", + "user_list": "Benutzer", + "login_list": "Logins", + "config": "Konfiguration", + "auth_user_list": "Benutzer" + }, + "admin": { + "settings": { + "header": "Konfiguration", + "website": { + "header": "Webseite", + "frontend_version": "Webseite Version", + "backend_version": "Server Version", + "config_path": "Konfigurations-Dateipfad", + "frontend_base_url": "Webseite Basis-URL", + "backend_base_url": "Server Basis-URL", + "token_expire_time": "Token Ablaufzeit", + "refresh_token_expire_time": "Refresh Token Ablaufzeit" + }, + "e_mail": { + "header": "E-Mail", + "user": "Benutzer", + "host": "Host", + "port": "Port", + "transceiver": "Absender", + "e_mail_address": "E-Mail Adresse", + "e_mail": "E-Mail", + "send_e_mail": "E-Mail senden" + }, + "message": { + "error": "Fehler", + "could_not_send_mail": "E-Mail konte nicht gesendet werden!", + "connection_failed": "Verbindung fehlgeschlagen", + "connection_to_mail_failed": "Die Verbindung zum Mailserver konnte nicht hergestellt werden!", + "mail_login_failed": "Die Anmeldung am Mailserver ist fehlgeschlagen!", + "send_failed": "Senden fehlgeschlagen", + "test_mail_not_send": "Die Test E-Mail konnte nicht gesendet werden!", + "success": "Erfolg", + "send_mail": "E-Mail wurde erfolgreich gesendet" + } + }, + "auth_users": { + "header": "Benutzer", + "of": "von", + "add": "Hinzufügen", + "reset_filters": "Filter zurücksetzen", + "users": "Benutzer", + "headers": { + "users": "Benutzer", + "first_name": "Vorname", + "last_name": "Nachname", + "e_mail": "E-Mail", + "active": "Aktiv", + "role": "Rolle", + "password": "Passwort", + "actions": "Aktionen" + }, + "no_entries_found": "Keine Einträge gefunden", + "message": { + "invalid_email": "Ungültige E-Mail", + "invalid_email_d": "Die E-Mail {{eMail}} ist nicht gültig!", + "user_already_exists": "Benutzer existiert bereits", + "user_already_exists_d": "Der Benutzer {{eMail}} existiert bereits!", + "user_added": "Benutzer hinzugefügt", + "user_added_d": "Benutzer {{eMail}} erfolgreich hinzugefügt", + "user_change_failed": "Benutzer änderung fehlgeschlagen", + "user_change_failed_d": "Benutzer {{eMail}} konnte nicht geändert werden!", + "user_changed": "Benutzer geändert", + "user_changed_d": "Benutzer {{eMail}} erfolgreich geändert", + "cannot_delete_user": "Benutzer kann nicht gelöscht werden", + "logon_with_another_user": "Loggen Sie sich mit einem anderen Benutzer ein, um diesen Benutzer zu löschen!", + "user_delete": "Benutzer löschen", + "user_delete_q": "Sind Sie sich sicher, dass Sie {{eMail}} löschen möchten?", + "user_deleted": "Benutzer gelöscht", + "user_deleted_d": "Benutzer {{eMail}} erfolgreich gelöscht" + } + } + }, + "auth": { + "header": "Krümmelmonster WI", + "login": {}, + "register": {}, + "forgot_password": { + "e_mail": "E-Mail", + "send_confirmation_url": "Falls ein Benutzer mit der E-Mail gefunden wurde, wurde Betstätigungslink versendet", + "reset_password": "Passwort zurücksetzen", + "login": "Anmelden", + "register": "Registrieren", + "repeat_password": "Passwort wiederholen", + "passwords_do_not_match": "Die Passwörter stimmen nicht überein", + "password": "Passwort", + "message": { + "reset_password": "Passwort zurückgesetzt", + "reset_password_d": "Dein Passwort wurde zurückgesetzt" + } + } + }, + "view": { + "dashboard": {}, + "user-list": {}, + "change-password": { + "header": "Passwort ändern", + "wrong_password": "Falsches Passwort", + "passwords_do_not_match": "Die Passwörter stimmen nicht überein", + "password": "Passwort", + "active_password": "Aktuelles Passwort", + "new_password": "Neues Passwort", + "repeat_new_password": "Neues Passwort wiederholen", + "save": "Speichern", + "message": { + "error": "Fehler", + "password_cannot_be_changed": "Dein Passwort konnte nicht geändert werden!", + "change_password": "Passwort geändert", + "changed_password": "Dein Passwort wurde geändert" + } + }, + "user_settings": { + "header": "Einstellungen", + "first_name": "Vorname", + "last_name": "Nachname", + "e_mail": "E-Mail", + "password": "Passwort", + "e_mail_already_exists": "Die E-Mail wurde bereits vergeben", + "wrong_password": "Falsches Passwort", + "save": "Speichern", + "message": { + "user_not_found": "Benutzer nicht gefunden", + "user_not_found_d": "Der Benutzer konnte nicht gefunden werden!", + "error": "Fehler", + "could_not_change_settings": "Die Einstellungen konnten nicht geändert werden!", + "changed_settings": "Die Einstellungen wurden geändert", + "success": "Erfolg" + } + } + }, + "footer": { + "imprint": "Impressum", + "backend": "Webseite", + "frontend": "API" + }, + "dialog": { + "confirm": "Bestätigen", + "abort": "Abbrechen" + }, + "general": { + "days": "Tage", + "minutes": "Minuten" + }, + "common": { + "bool_as_string": { + "true": "Ja", + "false": "Nein" + }, + "error": "Fehler", + "404": "404 - Der Eintrag konnte nicht gefunden werden" + }, + "primeng": { + "startsWith": "Startet mit", + "contains": "Enthält", + "notContains": "Enthält nicht", + "endsWith": "Ended mit", + "equals": "Gleich", + "notEquals": "Nicht gleich", + "noFilter": "Kein Filter", + "lt": "Weniger als", + "lte": "Weniger als oder gleich", + "gt": "Größer als", + "gte": "Größer als doer gleich", + "is": "Ist", + "isNot": "Ist nicht", + "before": "Vorher", + "after": "Nachher", + "clear": "Zurücksetzen", + "apply": "Anwenden", + "matchAll": "Passend zu allem", + "matchAny": "Passend zu jedem", + "addRule": "Regel hinzufügen", + "removeRule": "Regel entfernen", + "accept": "Ja", + "reject": "Nein", + "choose": "Wählen", + "upload": "Hochladen", + "cancel": "Abbrechen", + "dayNames": [ + "Sonntag", + "Montag", + "Dienstag", + "Mittwoch", + "Donnerstag", + "Freitag", + "Samstag" + ], + "dayNamesShort": [ + "Son", + "Mon", + "Die", + "Mit", + "Don", + "Fre", + "Sam" + ], + "dayNamesMin": [ + "So", + "Mo", + "Di", + "Mi", + "Do", + "Fr", + "Sa" + ], + "monthNames": [ + "Januar", + "Februar", + "März", + "April", + "Mai", + "Juni", + "Juli", + "August", + "September", + "Oktober", + "November", + "Dezember" + ], + "monthNamesShort": [ + "Jan", + "Feb", + "Mär", + "Apr", + "Mai", + "Jun", + "Jul", + "Aug", + "Sep", + "Okt", + "Nov", + "Dez" + ], + "today": "Heute", + "weekHeader": "Wk", + "weak": "Woche", + "medium": "Mittel", + "strong": "Stark", + "passwordPrompt": "Passwort eingeben", + "emptyMessage": "Keine Ergebnisse gefunden", + "emptyFilterMessage": "Keine Ergebnisse gefunden" + } +} \ No newline at end of file diff --git a/kdb-web/src/assets/i18n/en.json b/kdb-web/src/assets/i18n/en.json new file mode 100644 index 00000000..28a8c47b --- /dev/null +++ b/kdb-web/src/assets/i18n/en.json @@ -0,0 +1,322 @@ +{ + "header": { + "change_password": "Change Password", + "settings": "Settings", + "logout": "Logout" + }, + "sidebar": { + "dashboard": "Dashboard", + "domain_list": "Domains", + "host_list": "Hosts", + "user_list": "Users", + "login_list": "Logins", + "config": "Configuration", + "auth_user_list": "User" + }, + "admin": { + "settings": { + "header": "Configuration", + "website": { + "header": "Website", + "frontend_version": "Website version", + "backend_version": "Server version", + "config_path": "Configuration-Path", + "frontend_base_url": "Website Base-URL", + "backend_base_url": "Server Base-URL", + "token_expire_time": "Token expire time", + "refresh_token_expire_time": "Refresh Token expire time" + }, + "e_mail": { + "header": "E-Mail", + "user": "User", + "host": "Host", + "port": "Port", + "transceiver": "Sender", + "e_mail_address": "E-Mail address", + "e_mail": "E-Mail", + "send_e_mail": "Send E-Mail" + }, + "message": { + "error": "Error", + "could_not_send_mail": "E-mail could not be sent!", + "connection_failed": "Connection Failed", + "connection_to_mail_failed": "The connection to the mail server could not be established!", + "mail_login_failed": "The registration at the mail server failed!", + "send_failed": "Sending failed", + "test_mail_not_send": "The test email could not be sent!", + "success": "Success", + "send_mail": "Email was sent successfully" + } + }, + "auth_users": { + "header": "User", + "of": "of", + "add": "Add", + "reset_filters": "Reset filters", + "users": "User", + "headers": { + "users": "User", + "first_name": "Forename", + "last_name": "Surname", + "e_mail": "E-Mail", + "active": "Active", + "role": "Role", + "password": "Password", + "actions": "Actions" + }, + "no_entries_found": "No entries found", + "message": { + "invalid_email": "Invalid E-Mail", + "invalid_email_d": "The e-mail {{eMail}} is not valid!", + "user_already_exists": "User already exists", + "user_already_exists_d": "The user {{eMail}} already exists!", + "user_added": "User added", + "user_added_d": "User {{eMail}} successfully added", + "user_change_failed": "User change failed", + "user_change_failed_d": "User {{eMail}} could not be changed!", + "user_changed": "User changed", + "user_changed_d": "User {{eMail}} changed successfully", + "cannot_delete_user": "User cannot be deleted", + "logon_with_another_user": "Log in with another user to delete this user!", + "user_delete": "Delete user", + "user_delete_q": "Are you sure you want to delete {{eMail}}?", + "user_deleted": "User deleted", + "user_deleted_d": "User {{eMail}} successfully deleted" + } + } + }, + "auth": { + "header": "Login counter", + "login": { + "e_mail": "E-Mail", + "password": "Password", + "login": "Login", + "register": "Register", + "forgot_password": "Forgot password", + "e_mail_required": "E-Mail required", + "user_not_found": "User not found", + "e_mail_not_confirmed": "Email was not confirmed", + "password_required": "Passwort required", + "wrong_password": "Wrong password" + }, + "register": { + "first_name": "Forename", + "last_name": "Surname", + "e_mail": "E-Mail", + "repeat_e_mail": "Repeat E-mail", + "password": "Password", + "repeat_password": "Repeat password", + "register": "Register", + "login": "Login", + "user_already_exists": "User already exists", + "passwords_not_match": "The passwords do not match", + "e_mails_not_match": "The emails do not match", + "first_name_required": "Forename required", + "last_name_required": "Surname required", + "e_mail_required": "E-Mail required", + "password_required": "Passwort required", + "first_name_invalid": "Forename invalid", + "last_name_invalid": "Surname invalid" + }, + "forgot_password": { + "e_mail": "E-Mail", + "send_confirmation_url": "If a user was found with the email, a confirmation link was sent", + "reset_password": "Reset password", + "login": "Login", + "register": "Register", + "repeat_password": "Repeat password", + "passwords_do_not_match": "The passwords do not match", + "password": "Password", + "message": { + "reset_password": "Password reset", + "reset_password_d": "Your password has been reset" + } + } + }, + "view": { + "dashboard": {}, + "user-list": {}, + "change-password": { + "header": "Change Password", + "wrong_password": "Wrong password", + "passwords_do_not_match": "The passwords do not match", + "password": "Password", + "active_password": "Current Password", + "new_password": "New password", + "repeat_new_password": "Repeat new password", + "save": "Save", + "message": { + "error": "Error", + "password_cannot_be_changed": "Your password could not be changed!", + "change_password": "Changed password", + "changed_password": "Your password has been changed" + } + }, + "user_settings": { + "header": "Settings", + "first_name": "Forename", + "last_name": "Surname", + "e_mail": "E-Mail", + "password": "Password", + "e_mail_already_exists": "The email has already been taken", + "wrong_password": "Wrong password", + "save": "Save", + "message": { + "user_not_found": "User not found", + "user_not_found_d": "The user could not be found!", + "error": "Error", + "could_not_change_settings": "The settings could not be changed!", + "changed_settings": "Settings changed", + "success": "Success" + } + } + }, + "footer": { + "imprint": "Imprint", + "backend": "Website", + "frontend": "API" + }, + "dialog": { + "confirm": "Confirm", + "abort": "Abort" + }, + "general": { + "days": "Days", + "minutes": "Minutes" + }, + "login_models": { + "domain": { + "header": "Domain", + "name": "Name", + "users": "User", + "hosts": "Hosts", + "notify_when_login": "Notification after login", + "not_found": "Domain not found!" + }, + "host": { + "header": "Host", + "name": "Name", + "ip_address": "IP Address", + "users": "User", + "domain": "Domain", + "os": "Operating system", + "notify_when_login": "Notification after login", + "not_found": "Host not found!" + }, + "user": { + "header": "User", + "name": "Name", + "host": "Host", + "domain": "Domain", + "notify_when_login": "Notification after login", + "not_found": "User not found!" + }, + "login": { + "header": "Login", + "time": "Time", + "user": "User", + "host": "Host", + "notify_when_login": "Notification after login", + "not_found": "Login not found!" + } + }, + "common": { + "bool_as_string": { + "true": "Yes", + "false": "No" + }, + "error": "Error", + "404": "404 - Entry not found!" + }, + "primeng": { + "startsWith": "Starts with", + "contains": "Contains", + "notContains": "Not contains", + "endsWith": "Ends with", + "equals": "Equals", + "notEquals": "Not equals", + "noFilter": "No Filter", + "lt": "Less than", + "lte": "Less than or equal to", + "gt": "Greater than", + "gte": "Great then or equals", + "is": "Is", + "isNot": "Is not", + "before": "Before", + "after": "After", + "clear": "Clear", + "apply": "Apply", + "matchAll": "Match All", + "matchAny": "Match Any", + "addRule": "Add Rule", + "removeRule": "Remove Rule", + "accept": "Yes", + "reject": "No", + "choose": "Choose", + "upload": "Upload", + "cancel": "Cancel", + "dayNames": [ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" + ], + "dayNamesShort": [ + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat" + ], + "dayNamesMin": [ + "Su", + "Mo", + "Tu", + "We", + "Th", + "Fr", + "Sa" + ], + "monthNames": [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" + ], + "monthNamesShort": [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + ], + "today": "Today", + "weekHeader": "Wk", + "weak": "Weak", + "medium": "Medium", + "strong": "Strong", + "passwordPrompt": "Enter a password", + "emptyMessage": "No results found", + "emptyFilterMessage": "No results found" + } +} \ No newline at end of file diff --git a/kdb-web/src/assets/images/favicon.ico b/kdb-web/src/assets/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..997406ad22c29aae95893fb3d666c30258a09537 GIT binary patch literal 948 zcmV;l155mgP)CBYU7IjCFmI-B}4sMJt3^s9NVg!P0 z6hDQy(L`XWMkB@zOLgN$4KYz;j0zZxq9KKdpZE#5@k0crP^5f9KO};h)ZDQ%ybhht z%t9#h|nu0K(bJ ztIkhEr!*UyrZWQ1k2+YkGqDi8Z<|mIN&$kzpKl{cNP=OQzXHz>vn+c)F)zO|Bou>E z2|-d_=qY#Y+yOu1a}XI?cU}%04)zz%anD(XZC{#~WreV!a$7k2Ug`?&CUEc0EtrkZ zL49MB)h!_K{H(*l_93D5tO0;BUnvYlo+;yss%n^&qjt6fZOa+}+FDO(~2>G z2dx@=JZ?DHP^;b7*Y1as5^uphBsh*s*z&MBd?e@I>-9kU>63PjP&^#5YTOb&x^6Cf z?674rmSHB5Fk!{Gv7rv!?qX#ei_L(XtwVqLX3L}$MI|kJ*w(rhx~tc&L&xP#?cQow zX_|gx$wMr3pRZIIr_;;O|8fAjd;1`nOeu5K(pCu7>^3E&D2OBBq?sYa(%S?GwG&_0-s%_v$L@R!5H_fc)lOb9ZoOO#p`Nn`KU z3LTTBtjwo`7(HA6 z7gmO$yTR!5L>Bsg!X8616{JUngg_@&85%>W=mChTR;x4`P=?PJ~oPuy5 zU-L`C@_!34D21{fD~Y8NVnR3t;aqZI3fIhmgmx}$oc-dKDC6Ap$Gy>a!`A*x2L1v0 WcZ@i?LyX}70000CBYU7IjCFmI-B}4sMJt3^s9NVg!P0 z6hDQy(L`XWMkB@zOLgN$4KYz;j0zZxq9KKdpZE#5@k0crP^5f9KO};h)ZDQ%ybhht z%t9#h|nu0K(bJ ztIkhEr!*UyrZWQ1k2+YkGqDi8Z<|mIN&$kzpKl{cNP=OQzXHz>vn+c)F)zO|Bou>E z2|-d_=qY#Y+yOu1a}XI?cU}%04)zz%anD(XZC{#~WreV!a$7k2Ug`?&CUEc0EtrkZ zL49MB)h!_K{H(*l_93D5tO0;BUnvYlo+;yss%n^&qjt6fZOa+}+FDO(~2>G z2dx@=JZ?DHP^;b7*Y1as5^uphBsh*s*z&MBd?e@I>-9kU>63PjP&^#5YTOb&x^6Cf z?674rmSHB5Fk!{Gv7rv!?qX#ei_L(XtwVqLX3L}$MI|kJ*w(rhx~tc&L&xP#?cQow zX_|gx$wMr3pRZIIr_;;O|8fAjd;1`nOeu5K(pCu7>^3E&D2OBBq?sYa(%S?GwG&_0-s%_v$L@R!5H_fc)lOb9ZoOO#p`Nn`KU z3LTTBtjwo`7(HA6 z7gmO$yTR!5L>Bsg!X8616{JUngg_@&85%>W=mChTR;x4`P=?PJ~oPuy5 zU-L`C@_!34D21{fD~Y8NVnR3t;aqZI3fIhmgmx}$oc-dKDC6Ap$Gy>a!`A*x2L1v0 WcZ@i?LyX}70000 + + + + Krümmelmonster WI + + + + + + + + diff --git a/kdb-web/src/main.ts b/kdb-web/src/main.ts new file mode 100644 index 00000000..c7b673cf --- /dev/null +++ b/kdb-web/src/main.ts @@ -0,0 +1,12 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/kdb-web/src/polyfills.ts b/kdb-web/src/polyfills.ts new file mode 100644 index 00000000..429bb9ef --- /dev/null +++ b/kdb-web/src/polyfills.ts @@ -0,0 +1,53 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes recent versions of Safari, Chrome (including + * Opera), Edge on the desktop, and iOS and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js'; // Included with Angular CLI. + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git a/kdb-web/src/styles.scss b/kdb-web/src/styles.scss new file mode 100644 index 00000000..8964e79c --- /dev/null +++ b/kdb-web/src/styles.scss @@ -0,0 +1,394 @@ +@import "./styles/themes/default-light-theme.scss"; +@import "./styles/themes/default-dark-theme.scss"; +@import "./styles/themes/sh-edraft-dark-theme.scss"; +@import "./styles/themes/sh-edraft-light-theme.scss"; +@import "./styles/primeng-fixes.scss"; +@import "./styles/constants.scss"; + +html, +body { + height: 100%; + padding: 0; + margin: 0; +} + +main { + display: flex; + flex-direction: column; + min-height: 100vh; +} + +h1, +h2 { + margin: 0; + font-size: 30px; +} + +h2 { + margin: 0; + font-size: 25px; +} + +header { + height: $headerHeight; + display: flex; + flex-direction: row; + + .logo-button-wrapper { + display: flex; + align-items: center; + justify-content: center; + margin: 0px 5px; + + .p-button.p-button-text { + border: none; + } + } + + .logo { + display: flex; + align-items: center; + justify-content: center; + } + + .header-menu { + text-align: right; + flex: 1; + justify-content: flex-end; + } + + .p-menu-overlay { + width: 180px !important; + } +} + +.app { + height: 100%; + display: flex; + flex: 1; + + .sidebar { + height: 100%; + } + + h1 { + // table views with filters are scrollable with 10 items, when 30px margin + margin-bottom: 20px; + } + + .component-wrapper { + width: 100%; + + .component { + width: 100%; + height: 100%; + padding: 15px; + + .content-wrapper { + margin-bottom: 30px; + border-radius: 5px; + + .content-header { + padding: 10px; + + h2 { + font-size: 20px; + } + } + + .content { + width: 100%; + display: flex; + flex-direction: column; + padding: 15px; + + form { + width: 100%; + } + + .content-row { + display: flex; + flex-direction: row; + flex: 1; + margin: 1.5px 0px; + } + + .content-column { + display: flex; + flex: 1; + } + + .content-data-name { + display: flex; + flex: 1; + align-items: center; + + font-size: 18px; + } + + .content-data-value { + display: flex; + flex: 1; + align-items: center; + + font-size: 18px; + } + + .content-divider { + margin: 5px 0px; + } + + .content-input-field { + width: 50% !important; + margin: 0 !important; + } + + .input-field { + width: 100%; + + input, + .p-password { + width: 100%; + } + } + + .input-field-info-text { + margin: 15px 0px; + width: 240px; + } + + .login-form-submit { + .login-form-submit-btn { + width: 240px; + } + } + + .login-form-sub-button-wrapper { + display: flex; + flex-direction: column; + + .login-form-sub-btn { + margin-top: 15px; + width: 100%; + } + + .login-form-sub-btn-wrapper { + width: 100%; + } + } + + .table-caption { + display: flex; + + .table-caption-text { + flex: 1; + font-weight: 400; + } + + .table-caption-search { + } + } + + .table-header-label { + display: flex; + + .table-header-text { + flex: 1; + } + + .table-header-icon { + } + } + + .table-header-actions { + width: 100px; + } + + .table-header-small-dropdown { + width: 150px; + } + } + } + } + } +} + +.p-dialog-header { + padding: 20px 20px 20px 20px !important; +} + +.p-dialog-content { + padding: 20px !important; +} + +.p-dialog-footer { + padding: 0px 20px 20px 20px !important; +} + +.p-dialog-content { + .content-row { + display: flex; + flex-direction: row; + flex: 1; + margin: 1.5px 0px; + } + + .content-column { + display: flex; + flex: 1; + } + + .content-data-name { + display: flex; + flex: 1; + align-items: center; + + font-size: 18px; + } + + .content-data-value { + display: flex; + flex: 1; + align-items: center; + + font-size: 18px; + } + + .content-divider { + margin: 5px 0px; + } + + .content-input-field { + width: 50% !important; + margin: 0 !important; + } +} + +footer { + width: 100%; + height: $footerHeight; + padding: 0px 10px; + + display: flex; + align-items: center; + justify-content: center; + + .left { + width: 50%; + + display: flex; + + // .frontend-version { + // } + + .version-divider { + margin: 0px 5px; + } + + // .backend-version { + // } + } + + .right { + width: 50%; + text-align: right; + } +} + +.login-wrapper { + width: 100vw; + height: 100vh; + + display: flex; + justify-content: center; + align-items: center; + + .login-form-wrapper { + width: 350px; + height: 350px; + + display: flex; + justify-content: center; + align-items: center; + + border-radius: 25px; + + .login-form { + width: 80%; + height: 80%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + h1 { + text-align: center; + } + + .input-field-info-text { + margin: 15px 0px; + width: 240px; + } + + .login-form-submit { + .login-form-submit-btn { + width: 240px; + } + } + + .login-form-sub-button-wrapper { + display: flex; + flex-direction: column; + + .login-form-sub-btn { + margin-top: 15px; + width: 100%; + } + + .login-form-sub-btn-wrapper { + width: 100%; + } + } + } + } + + .register-form-wrapper { + height: 550px; + } + + .register-confirm-form-wrapper { + height: 250px; + } +} + +.input-field { + margin: 15px 0px; + + input, + .p-password { + height: 40px; + width: 240px; + font-size: 18px; + } +} + +.btn { + border: 0; +} + +.spinner-component-wrapper { + position: absolute; + top: 0; + width: 100vw; + height: 100vh; + + display: flex; + flex-direction: row; + justify-content: center; + + .spinner-wrapper { + display: flex; + justify-content: center; + align-items: center; + } +} diff --git a/kdb-web/src/styles/constants.scss b/kdb-web/src/styles/constants.scss new file mode 100644 index 00000000..570127bb --- /dev/null +++ b/kdb-web/src/styles/constants.scss @@ -0,0 +1,2 @@ +$headerHeight: 50px; +$footerHeight: 25px; \ No newline at end of file diff --git a/kdb-web/src/styles/primeng-fixes.scss b/kdb-web/src/styles/primeng-fixes.scss new file mode 100644 index 00000000..560ad4e3 --- /dev/null +++ b/kdb-web/src/styles/primeng-fixes.scss @@ -0,0 +1,71 @@ +@import "./constants.scss"; + +.btn { + &:focus { + box-shadow: none !important; + } +} + +.p-menu { + background: none !important; + border: none !important; + width: auto !important; + border-radius: 0px !important; + padding: 0 !important; + + .p-menuitem-link { + $distance: 10px; + padding: $distance 0px $distance $distance !important; + margin: 4px 0px 4px 6px !important; + } +} + +.p-menu-overlay { + top: $headerHeight !important; +} + +ui-menu .ui-menu-parent .ui-menu-child { + width: 400px; /* exagerated !! */ +} + +.p-toast-detail { + white-space: pre-line; +} + +.p-datatable .p-sortable-column:focus { + box-shadow: none !important; +} + +.p-password { + padding: 0px !important; +} + +.p-paginator { + background-color: transparent !important; + border: none !important; +} + +p-table { + .p-datatable .p-datatable-header { + border: none !important; + } + + .p-datatable .p-datatable-header, + .p-datatable .p-datatable-tbody > tr, + .p-datatable .p-datatable-thead > tr > th { + background-color: transparent !important; + color: inherit !important; + } +} + +.pi-sort-alt:before { + content: "\e915" !important; +} + +.pi-sort-amount-up-alt:before { + content: "\e914" !important; +} + +.pi-sort-amount-down:before { + content: "\e913" !important; +} diff --git a/kdb-web/src/styles/themes/default-dark-theme.scss b/kdb-web/src/styles/themes/default-dark-theme.scss new file mode 100644 index 00000000..7507a6cb --- /dev/null +++ b/kdb-web/src/styles/themes/default-dark-theme.scss @@ -0,0 +1,517 @@ +.default-dark-theme { + $primaryTextColor: #fff; + $secondayTextColor: #000; + $secondayTextColor2: #1e88e5; + + $primaryHeaderColor: #1e88e5; + $secondaryHeaderColor: #6ab7ff; + $secondaryHeaderColor2: #005cb2; + + $primaryBackgroundColor: #272727; + $secondaryBackgroundColor: #4f4f4f; + $secondaryBackgroundColor2: #fff; + $secondaryBackgroundColor3: #cccccc; + + $primaryErrorColor: #b00020; + $secondaryErrorColor: #e94948; + + $default-border: 1px solid $secondaryBackgroundColor3; + + background-color: $primaryBackgroundColor !important; + + html, + body { + margin: 0; + + font-size: 16px; + } + + h1, + h2 { + color: $primaryHeaderColor; + } + + input, + p { + color: $primaryTextColor; + } + + input { + background-color: $secondaryBackgroundColor !important; + } + + .input-field-info-text { + color: $primaryTextColor; + } + + /* Change Autocomplete styles in Chrome*/ + input:-webkit-autofill, + input:-webkit-autofill:hover, + input:-webkit-autofill:focus, + textarea:-webkit-autofill, + textarea:-webkit-autofill:hover, + textarea:-webkit-autofill:focus, + select:-webkit-autofill, + select:-webkit-autofill:hover, + select:-webkit-autofill:focus { + border: 1px solid $primaryHeaderColor; + -webkit-text-fill-color: $primaryTextColor; + -webkit-box-shadow: 0 0 0px 1000px $secondaryBackgroundColor inset; + transition: background-color 5000s ease-in-out 0s; + } + + header { + background-color: $primaryBackgroundColor; + + .logo-button-wrapper { + .p-button.p-button-text { + color: $primaryTextColor; + + &:hover { + color: $primaryTextColor; + background-color: $primaryBackgroundColor; + } + } + } + + .logo { + color: $primaryHeaderColor; + } + } + + h1 { + color: $primaryHeaderColor; + } + + .app { + .sidebar { + background-color: $primaryBackgroundColor; + + .menu { + color: $primaryTextColor; + } + } + + .component-wrapper { + color: $primaryTextColor; + + .component { + background-color: $secondaryBackgroundColor; + + .content-wrapper { + background-color: $primaryBackgroundColor; + border-top: 2px solid $primaryHeaderColor; + + .content-header { + border-bottom: $default-border; + } + + .content { + .content-row { + } + + .content-column { + } + + .content-data-name { + } + + .content-data-value { + } + + .content-divider { + border-bottom: $default-border; + } + } + } + } + } + } + + .p-dialog-header { + background-color: $secondaryBackgroundColor !important; + color: $primaryTextColor !important; + } + + .p-dialog-content { + .content-data-name, + .content-data-value { + color: $primaryTextColor; + + .text-btn { + font-size: 18px !important; + font-weight: 400 !important; + } + } + } + + footer { + background-color: $primaryBackgroundColor; + color: $primaryTextColor; + + a { + color: $primaryTextColor; + } + } + + .invalid-feedback { + color: $primaryErrorColor; + } + + .invalid-feedback-input, + .invalid-feedback-input:focus, + .invalid-feedback-input:hover { + input, + input:enabled:focus { + outline: 1px solid $primaryErrorColor !important; + border: 1px solid $primaryErrorColor !important; + border-color: $primaryErrorColor !important; + } + } + + .login-wrapper { + background-color: $secondaryBackgroundColor; + + .login-form-wrapper { + background-color: $primaryBackgroundColor; + + .login-form { + .input-field { + input, + .p-password { + } + } + + .login-form-submit { + .login-form-submit-btn { + } + } + + .login-form-sub-button-wrapper { + .login-form-sub-btn { + background-color: $primaryBackgroundColor; + color: $secondayTextColor2; + border: 2px solid $secondayTextColor2; + + &:hover { + background-color: $primaryHeaderColor !important; + color: $primaryTextColor !important; + } + } + + .login-form-sub-login-btn { + border: none; + } + } + } + } + } + + .spinner-component-wrapper { + background-color: rgba($secondaryBackgroundColor, 0.5); + + .spinner-wrapper { + .custom-spinner .p-progress-spinner-circle { + color: $primaryHeaderColor; + } + } + } + + .wrapper-right { + justify-content: flex-end !important; + } + + /* + PrimeNG Fixes + */ + .p-progress-spinner-circle { + stroke: $primaryHeaderColor !important; + } + + .p-menu { + color: $primaryTextColor !important; + + .p-menuitem-link .p-menuitem-text, + .p-menuitem-link .p-menuitem-icon { + color: $primaryTextColor !important; + } + + .p-menuitem-link:focus { + box-shadow: none !important; + } + + .p-menuitem-link:hover { + background-color: $secondaryBackgroundColor !important; + $border-radius: 20px; + border-radius: $border-radius 0px 0px $border-radius; + + .p-menuitem-text, + .p-menuitem-icon { + color: $primaryHeaderColor !important; + } + } + } + + .p-menu-overlay { + background-color: $primaryBackgroundColor !important; + color: $primaryTextColor !important; + border-top: 2px solid $primaryHeaderColor !important; + + .p-menuitem-link:hover { + background-color: $primaryBackgroundColor !important; + .p-menuitem-text, + .p-menuitem-icon { + color: $primaryHeaderColor !important; + } + } + } + + p-dropdown { + .p-dropdown { + background-color: $primaryBackgroundColor !important; + border-color: $primaryTextColor !important; + color: $primaryTextColor !important; + + span { + color: $primaryTextColor; + } + + .p-dropdown-panel { + background-color: $secondaryBackgroundColor !important; + + .p-dropdown-items .p-dropdown-item:not(.p-highlight):not(.p-disabled):hover { + background-color: $secondaryBackgroundColor !important; + color: $primaryHeaderColor !important; + + span { + color: $primaryHeaderColor !important; + } + } + + .p-dropdown-items .p-dropdown-item.p-highlight { + background-color: $secondaryBackgroundColor !important; + color: $primaryHeaderColor !important; + + span { + color: $primaryHeaderColor !important; + } + } + } + } + } + + p-table { + background-color: $primaryBackgroundColor; + color: $primaryTextColor !important; + + .table-caption { + .table-caption-text { + } + + .table-caption-search-wrapper { + .table-caption-search { + height: 30px !important; + + .table-caption-search-icon { + } + + .table-caption-search-input { + height: 100% !important; + + &:focus { + outline-color: $primaryHeaderColor; + } + } + } + } + + .table-caption-btn-wrapper { + height: 30px !important; + } + } + + .table-edit-input { + width: 100% !important; + } + } + + p-checkbox { + .p-checkbox .p-checkbox-box { + background: $primaryBackgroundColor !important; + background-color: $primaryBackgroundColor !important; + } + + .p-checkbox .p-checkbox-box.p-highlight { + background: $primaryBackgroundColor !important; + background-color: $primaryBackgroundColor !important; + border-color: $primaryTextColor !important; + box-shadow: none !important; + } + + .p-checkbox .p-checkbox-box .p-checkbox-icon { + color: $primaryTextColor !important; + } + + .p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box.p-focus, + .p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box:hover { + border-color: $primaryHeaderColor !important; + box-shadow: none !important; + + .p-checkbox-icon { + color: $primaryHeaderColor !important; + } + } + } + + p-dialog, + p-confirmdialog, + p-dynamicdialog { + .p-dialog.p-confirm-dialog .p-confirm-dialog-message { + margin-left: 0px !important; + } + + .p-dialog { + background-color: $secondaryBackgroundColor !important; + + .p-dialog-header { + background-color: $primaryBackgroundColor !important; + color: $primaryTextColor !important; + } + + .p-dialog-content, + .p-dialog-footer { + background-color: $secondaryBackgroundColor !important; + color: $primaryTextColor !important; + } + } + } + + input, + .p-password { + &:focus { + box-shadow: none !important; + } + + &:hover, + &:active, + &:enabled:focus { + border-color: $primaryHeaderColor !important; + } + } + + .btn-wrapper { + display: flex !important; + align-items: center !important; + } + + .btn { + background-color: $primaryHeaderColor; + color: $primaryTextColor; + border: 0; + + &:hover, + &:enabled:hover { + background-color: $primaryHeaderColor; + color: $primaryTextColor; + border: 0; + } + } + + .icon-btn { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + + .pi { + font-size: 1.275rem !important; + } + } + + .text-btn { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + padding: 0px !important; + + &:hover { + background-color: transparent !important; + color: $primaryHeaderColor !important; + border: 0; + } + } + + .icon-btn-without-hover { + &:hover { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + } + } + + .icon-btn { + &:hover { + background-color: transparent !important; + color: $primaryHeaderColor !important; + border: 0; + } + } + + .danger-icon-btn { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + + &:hover { + background-color: transparent !important; + color: $primaryErrorColor !important; + border: 0; + } + + .pi { + font-size: 1.275rem !important; + } + } + + .danger-btn { + background-color: $primaryErrorColor !important; + color: $primaryErrorColor !important; + border: 0 !important; + + &:hover { + background-color: $primaryErrorColor !important; + color: $primaryTextColor !important; + border: 0; + } + + .pi { + font-size: 1.275rem !important; + } + } + + .p-datatable .p-sortable-column.p-highlight, + .p-datatable .p-sortable-column.p-highlight .p-sortable-column-icon { + color: $primaryHeaderColor !important; + } + + .p-dropdown:not(.p-disabled):hover, + .p-dropdown:not(.p-disabled).p-focus, + .p-link:focus { + border-color: $primaryHeaderColor !important; + box-shadow: none !important; + } + + .p-paginator .p-paginator-pages .p-paginator-page.p-highlight { + background: transparent !important; + background-color: transparent !important; + color: $primaryHeaderColor !important; + } + + .p-paginator .p-paginator-pages .p-paginator-page:not(.p-highlight):hover, + .p-paginator .p-paginator-first:not(.p-disabled):not(.p-highlight):hover, + .p-paginator .p-paginator-prev:not(.p-disabled):not(.p-highlight):hover, + .p-paginator .p-paginator-next:not(.p-disabled):not(.p-highlight):hover, + .p-paginator .p-paginator-last:not(.p-disabled):not(.p-highlight):hover { + background-color: $primaryBackgroundColor !important; + color: $primaryHeaderColor !important; + } +} diff --git a/kdb-web/src/styles/themes/default-light-theme.scss b/kdb-web/src/styles/themes/default-light-theme.scss new file mode 100644 index 00000000..5993a058 --- /dev/null +++ b/kdb-web/src/styles/themes/default-light-theme.scss @@ -0,0 +1,515 @@ +.default-light-theme { + $primaryTextColor: #272727; + $secondayTextColor: #fff; + $secondayTextColor2: #1e88e5; + + $primaryHeaderColor: #1e88e5; + $secondaryHeaderColor: #6ab7ff; + $secondaryHeaderColor2: #005cb2; + + $primaryBackgroundColor: #fff; + $secondaryBackgroundColor: #cccccc; + $secondaryBackgroundColor2: #4f4f4f; + $secondaryBackgroundColor3: #272727; + + $primaryErrorColor: #b00020; + $secondaryErrorColor: #e94948; + + $default-border: 1px solid $secondaryBackgroundColor; + + html, + body { + margin: 0; + + font-size: 16px; + } + + h1, + h2 { + color: $primaryHeaderColor; + } + + input, + p { + color: $primaryTextColor; + } + + input { + background-color: $primaryBackgroundColor !important; + } + + .input-field-info-text { + color: $primaryTextColor; + } + + /* Change Autocomplete styles in Chrome*/ + input:-webkit-autofill, + input:-webkit-autofill:hover, + input:-webkit-autofill:focus, + textarea:-webkit-autofill, + textarea:-webkit-autofill:hover, + textarea:-webkit-autofill:focus, + select:-webkit-autofill, + select:-webkit-autofill:hover, + select:-webkit-autofill:focus { + border: 1px solid $primaryHeaderColor; + -webkit-text-fill-color: $primaryTextColor; + -webkit-box-shadow: 0 0 0px 1000px $primaryBackgroundColor inset; + transition: background-color 5000s ease-in-out 0s; + } + + header { + background-color: $primaryBackgroundColor; + + .logo-button-wrapper { + .p-button.p-button-text { + color: $primaryTextColor; + + &:hover { + color: $primaryTextColor; + background-color: $primaryBackgroundColor; + } + } + } + + .logo { + color: $primaryHeaderColor; + } + } + + h1 { + color: $primaryHeaderColor; + } + + .app { + .sidebar { + background-color: $primaryBackgroundColor; + + .menu { + color: $primaryTextColor; + } + } + + .component-wrapper { + color: $primaryTextColor; + + .component { + background-color: $secondaryBackgroundColor; + + .content-wrapper { + background-color: $primaryBackgroundColor; + border-top: 2px solid $primaryHeaderColor; + + .content-header { + border-bottom: $default-border; + } + + .content { + .content-row { + } + + .content-column { + } + + .content-data-name { + } + + .content-data-value { + } + + .content-divider { + border-bottom: $default-border; + } + } + } + } + } + } + + .p-dialog-header { + background-color: $secondaryBackgroundColor !important; + color: $primaryTextColor !important; + } + + .p-dialog-content { + .content-data-name, + .content-data-value { + color: $primaryTextColor; + + .text-btn { + font-size: 18px !important; + font-weight: 400 !important; + } + } + } + + footer { + background-color: $primaryBackgroundColor; + color: $primaryTextColor; + + a { + color: $primaryTextColor; + } + } + + .invalid-feedback { + color: $primaryErrorColor; + } + + .invalid-feedback-input, + .invalid-feedback-input:focus, + .invalid-feedback-input:hover { + input, + input:enabled:focus { + outline: 1px solid $primaryErrorColor !important; + border: 1px solid $primaryErrorColor !important; + border-color: $primaryErrorColor !important; + } + } + + .login-wrapper { + background-color: $secondaryBackgroundColor; + + .login-form-wrapper { + background-color: $primaryBackgroundColor; + + .login-form { + .input-field { + input, + .p-password { + } + } + + .login-form-submit { + .login-form-submit-btn { + } + } + + .login-form-sub-button-wrapper { + .login-form-sub-btn { + background-color: $primaryBackgroundColor; + color: $secondayTextColor2; + border: 2px solid $secondayTextColor2; + + &:hover { + background-color: $primaryHeaderColor !important; + color: $primaryTextColor !important; + } + } + + .login-form-sub-login-btn { + border: none; + } + } + } + } + } + + .spinner-component-wrapper { + background-color: rgba($secondaryBackgroundColor, 0.5); + + .spinner-wrapper { + .custom-spinner .p-progress-spinner-circle { + color: $primaryHeaderColor; + } + } + } + + .wrapper-right { + justify-content: flex-end !important; + } + + /* + PrimeNG Fixes + */ + .p-progress-spinner-circle { + stroke: $primaryHeaderColor !important; + } + + .p-menu { + color: $primaryTextColor !important; + + .p-menuitem-link .p-menuitem-text, + .p-menuitem-link .p-menuitem-icon { + color: $primaryTextColor !important; + } + + .p-menuitem-link:focus { + box-shadow: none !important; + } + + .p-menuitem-link:hover { + background-color: $secondaryBackgroundColor !important; + $border-radius: 20px; + border-radius: $border-radius 0px 0px $border-radius; + + .p-menuitem-text, + .p-menuitem-icon { + color: $primaryHeaderColor !important; + } + } + } + + .p-menu-overlay { + background-color: $primaryBackgroundColor !important; + color: $primaryTextColor !important; + border-top: 2px solid $primaryHeaderColor !important; + + .p-menuitem-link:hover { + background-color: $primaryBackgroundColor !important; + .p-menuitem-text, + .p-menuitem-icon { + color: $primaryHeaderColor !important; + } + } + } + + p-dropdown { + .p-dropdown { + background-color: $primaryBackgroundColor !important; + border-color: $primaryTextColor !important; + color: $primaryTextColor !important; + + span { + color: $primaryTextColor; + } + + .p-dropdown-panel { + background-color: $primaryBackgroundColor !important; + + .p-dropdown-items .p-dropdown-item:not(.p-highlight):not(.p-disabled):hover { + background-color: $primaryBackgroundColor !important; + color: $primaryHeaderColor !important; + + span { + color: $primaryHeaderColor !important; + } + } + + .p-dropdown-items .p-dropdown-item.p-highlight { + background-color: $primaryBackgroundColor !important; + color: $primaryHeaderColor !important; + + span { + color: $primaryHeaderColor !important; + } + } + } + } + } + + p-table { + background-color: $primaryBackgroundColor; + color: $primaryTextColor !important; + + .table-caption { + .table-caption-text { + } + + .table-caption-search-wrapper { + .table-caption-search { + height: 30px !important; + + .table-caption-search-icon { + } + + .table-caption-search-input { + height: 100% !important; + + &:focus { + outline-color: $primaryHeaderColor; + } + } + } + } + + .table-caption-btn-wrapper { + height: 30px !important; + } + } + + .table-edit-input { + width: 100% !important; + } + } + + p-checkbox { + .p-checkbox .p-checkbox-box { + background: $primaryBackgroundColor !important; + background-color: $primaryBackgroundColor !important; + } + + .p-checkbox .p-checkbox-box.p-highlight { + background: $primaryBackgroundColor !important; + background-color: $primaryBackgroundColor !important; + border-color: $primaryTextColor !important; + box-shadow: none !important; + } + + .p-checkbox .p-checkbox-box .p-checkbox-icon { + color: $primaryTextColor !important; + } + + .p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box.p-focus, + .p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box:hover { + border-color: $primaryHeaderColor !important; + box-shadow: none !important; + + .p-checkbox-icon { + color: $primaryHeaderColor !important; + } + } + } + + p-dialog, + p-confirmdialog, + p-dynamicdialog { + .p-dialog.p-confirm-dialog .p-confirm-dialog-message { + margin-left: 0px !important; + } + + .p-dialog { + background-color: $primaryBackgroundColor !important; + + .p-dialog-header { + background-color: $secondaryBackgroundColor !important; + color: $primaryTextColor !important; + } + + .p-dialog-content, + .p-dialog-footer { + background-color: $primaryBackgroundColor !important; + color: $primaryTextColor !important; + } + } + } + + input, + .p-password { + &:focus { + box-shadow: none !important; + } + + &:hover, + &:active, + &:enabled:focus { + border-color: $primaryHeaderColor !important; + } + } + + .btn-wrapper { + display: flex !important; + align-items: center !important; + } + + .btn { + background-color: $primaryHeaderColor; + color: $primaryTextColor; + border: 0; + + &:hover, + &:enabled:hover { + background-color: $primaryHeaderColor; + color: $primaryTextColor; + border: 0; + } + } + + .icon-btn { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + + .pi { + font-size: 1.275rem !important; + } + } + + .text-btn { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + padding: 0px !important; + + &:hover { + background-color: transparent !important; + color: $primaryHeaderColor !important; + border: 0; + } + } + + .icon-btn-without-hover { + &:hover { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + } + } + + .icon-btn { + &:hover { + background-color: transparent !important; + color: $primaryHeaderColor !important; + border: 0; + } + } + + .danger-icon-btn { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + + &:hover { + background-color: transparent !important; + color: $primaryErrorColor !important; + border: 0; + } + + .pi { + font-size: 1.275rem !important; + } + } + + .danger-btn { + background-color: $primaryErrorColor !important; + color: $primaryErrorColor !important; + border: 0 !important; + + &:hover { + background-color: $primaryErrorColor !important; + color: $primaryTextColor !important; + border: 0; + } + + .pi { + font-size: 1.275rem !important; + } + } + + .p-datatable .p-sortable-column.p-highlight, + .p-datatable .p-sortable-column.p-highlight .p-sortable-column-icon { + color: $primaryHeaderColor !important; + } + + .p-dropdown:not(.p-disabled):hover, + .p-dropdown:not(.p-disabled).p-focus, + .p-link:focus { + border-color: $primaryHeaderColor !important; + box-shadow: none !important; + } + + .p-paginator .p-paginator-pages .p-paginator-page.p-highlight { + background: transparent !important; + background-color: transparent !important; + color: $primaryHeaderColor !important; + } + + .p-paginator .p-paginator-pages .p-paginator-page:not(.p-highlight):hover, + .p-paginator .p-paginator-first:not(.p-disabled):not(.p-highlight):hover, + .p-paginator .p-paginator-prev:not(.p-disabled):not(.p-highlight):hover, + .p-paginator .p-paginator-next:not(.p-disabled):not(.p-highlight):hover, + .p-paginator .p-paginator-last:not(.p-disabled):not(.p-highlight):hover { + background-color: $primaryBackgroundColor !important; + color: $primaryHeaderColor !important; + } +} diff --git a/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss b/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss new file mode 100644 index 00000000..ee2da9f4 --- /dev/null +++ b/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss @@ -0,0 +1,519 @@ +.sh-edraft-dark-theme { + $primaryTextColor: #fff; + $secondayTextColor: #000; + $secondayTextColor2: #ef9d0d; + + $primaryHeaderColor: #ef9d0d; + $secondaryHeaderColor: #ffce4c; + $secondaryHeaderColor2: #b76f00; + + $primaryBackgroundColor: #272727; + $secondaryBackgroundColor: #4f4f4f; + $secondaryBackgroundColor2: #fff; + $secondaryBackgroundColor3: #cccccc; + + $primaryErrorColor: #b00020; + $secondaryErrorColor: #e94948; + + $default-border: 1px solid $secondaryBackgroundColor3; + + background-color: $primaryBackgroundColor !important; + + html, + body { + margin: 0; + + font-size: 16px; + } + + h1, + h2 { + color: $primaryHeaderColor; + } + + input, + p { + color: $primaryTextColor; + } + + input { + background-color: $secondaryBackgroundColor !important; + } + + .input-field-info-text { + color: $primaryTextColor; + } + + /* Change Autocomplete styles in Chrome*/ + input:-webkit-autofill, + input:-webkit-autofill:hover, + input:-webkit-autofill:focus, + textarea:-webkit-autofill, + textarea:-webkit-autofill:hover, + textarea:-webkit-autofill:focus, + select:-webkit-autofill, + select:-webkit-autofill:hover, + select:-webkit-autofill:focus { + border: 1px solid $primaryHeaderColor; + -webkit-text-fill-color: $primaryTextColor; + -webkit-box-shadow: 0 0 0px 1000px $secondaryBackgroundColor inset; + transition: background-color 5000s ease-in-out 0s; + } + + header { + background-color: $primaryBackgroundColor; + + .logo-button-wrapper { + .p-button.p-button-text { + color: $primaryTextColor; + + &:hover { + color: $primaryTextColor; + background-color: $primaryBackgroundColor; + } + } + } + + .logo { + color: $primaryHeaderColor; + } + } + + h1 { + color: $primaryHeaderColor; + } + + .app { + .sidebar { + background-color: $primaryBackgroundColor; + + .menu { + color: $primaryTextColor; + } + } + + .component-wrapper { + color: $primaryTextColor; + + .component { + background-color: $secondaryBackgroundColor; + + .content-wrapper { + background-color: $primaryBackgroundColor; + border-top: 2px solid $primaryHeaderColor; + + .content-header { + border-bottom: $default-border; + } + + .content { + .content-row { + } + + .content-column { + } + + .content-data-name { + } + + .content-data-value { + } + + .content-divider { + border-bottom: $default-border; + } + } + } + } + } + } + + .p-dialog-header { + background-color: $primaryBackgroundColor !important; + color: $primaryTextColor !important; + } + + .p-dialog-content { + background-color: $secondaryBackgroundColor !important; + + .content-data-name, + .content-data-value { + color: $primaryTextColor; + + .text-btn { + font-size: 18px !important; + font-weight: 400 !important; + } + } + } + + footer { + background-color: $primaryBackgroundColor; + color: $primaryTextColor; + + a { + color: $primaryTextColor; + } + } + + .invalid-feedback { + color: $primaryErrorColor; + } + + .invalid-feedback-input, + .invalid-feedback-input:focus, + .invalid-feedback-input:hover { + input, + input:enabled:focus { + outline: 1px solid $primaryErrorColor !important; + border: 1px solid $primaryErrorColor !important; + border-color: $primaryErrorColor !important; + } + } + + .login-wrapper { + background-color: $secondaryBackgroundColor; + + .login-form-wrapper { + background-color: $primaryBackgroundColor; + + .login-form { + .input-field { + input, + .p-password { + } + } + + .login-form-submit { + .login-form-submit-btn { + } + } + + .login-form-sub-button-wrapper { + .login-form-sub-btn { + background-color: $primaryBackgroundColor; + color: $secondayTextColor2; + border: 2px solid $secondayTextColor2; + + &:hover { + background-color: $primaryHeaderColor !important; + color: $primaryTextColor !important; + } + } + + .login-form-sub-login-btn { + border: none; + } + } + } + } + } + + .spinner-component-wrapper { + background-color: rgba($secondaryBackgroundColor, 0.5); + + .spinner-wrapper { + .custom-spinner .p-progress-spinner-circle { + color: $primaryHeaderColor; + } + } + } + + .wrapper-right { + justify-content: flex-end !important; + } + + /* + PrimeNG Fixes + */ + .p-progress-spinner-circle { + stroke: $primaryHeaderColor !important; + } + + .p-menu { + color: $primaryTextColor !important; + + .p-menuitem-link .p-menuitem-text, + .p-menuitem-link .p-menuitem-icon { + color: $primaryTextColor !important; + } + + .p-menuitem-link:focus { + box-shadow: none !important; + } + + .p-menuitem-link:hover { + background-color: $secondaryBackgroundColor !important; + $border-radius: 20px; + border-radius: $border-radius 0px 0px $border-radius; + + .p-menuitem-text, + .p-menuitem-icon { + color: $primaryHeaderColor !important; + } + } + } + + .p-menu-overlay { + background-color: $primaryBackgroundColor !important; + color: $primaryTextColor !important; + border-top: 2px solid $primaryHeaderColor !important; + + .p-menuitem-link:hover { + background-color: $primaryBackgroundColor !important; + .p-menuitem-text, + .p-menuitem-icon { + color: $primaryHeaderColor !important; + } + } + } + + p-dropdown { + .p-dropdown { + background-color: $primaryBackgroundColor !important; + border-color: $primaryTextColor !important; + color: $primaryTextColor !important; + + span { + color: $primaryTextColor; + } + + .p-dropdown-panel { + background-color: $secondaryBackgroundColor !important; + + .p-dropdown-items .p-dropdown-item:not(.p-highlight):not(.p-disabled):hover { + background-color: $secondaryBackgroundColor !important; + color: $primaryHeaderColor !important; + + span { + color: $primaryHeaderColor !important; + } + } + + .p-dropdown-items .p-dropdown-item.p-highlight { + background-color: $secondaryBackgroundColor !important; + color: $primaryHeaderColor !important; + + span { + color: $primaryHeaderColor !important; + } + } + } + } + } + + p-table { + background-color: $primaryBackgroundColor; + color: $primaryTextColor !important; + + .table-caption { + .table-caption-text { + } + + .table-caption-search-wrapper { + .table-caption-search { + height: 30px !important; + + .table-caption-search-icon { + } + + .table-caption-search-input { + height: 100% !important; + + &:focus { + outline-color: $primaryHeaderColor; + } + } + } + } + + .table-caption-btn-wrapper { + height: 30px !important; + } + } + + .table-edit-input { + width: 100% !important; + } + } + + p-checkbox { + .p-checkbox .p-checkbox-box { + background: $primaryBackgroundColor !important; + background-color: $primaryBackgroundColor !important; + } + + .p-checkbox .p-checkbox-box.p-highlight { + background: $primaryBackgroundColor !important; + background-color: $primaryBackgroundColor !important; + border-color: $primaryTextColor !important; + box-shadow: none !important; + } + + .p-checkbox .p-checkbox-box .p-checkbox-icon { + color: $primaryTextColor !important; + } + + .p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box.p-focus, + .p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box:hover { + border-color: $primaryHeaderColor !important; + box-shadow: none !important; + + .p-checkbox-icon { + color: $primaryHeaderColor !important; + } + } + } + + p-dialog, + p-confirmdialog, + p-dynamicdialog { + .p-dialog.p-confirm-dialog .p-confirm-dialog-message { + margin-left: 0px !important; + } + + .p-dialog { + background-color: $secondaryBackgroundColor !important; + + .p-dialog-header { + background-color: $primaryBackgroundColor !important; + color: $primaryTextColor !important; + } + + .p-dialog-content, + .p-dialog-footer { + background-color: $secondaryBackgroundColor !important; + color: $primaryTextColor !important; + } + } + } + + input, + .p-password { + &:focus { + box-shadow: none !important; + } + + &:hover, + &:active, + &:enabled:focus { + border-color: $primaryHeaderColor !important; + } + } + + .btn-wrapper { + display: flex !important; + align-items: center !important; + } + + .btn { + background-color: $primaryHeaderColor; + color: $primaryTextColor; + border: 0; + + &:hover, + &:enabled:hover { + background-color: $primaryHeaderColor; + color: $primaryTextColor; + border: 0; + } + } + + .icon-btn { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + + .pi { + font-size: 1.275rem !important; + } + } + + .text-btn { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + padding: 0px !important; + + &:hover { + background-color: transparent !important; + color: $primaryHeaderColor !important; + border: 0; + } + } + + .icon-btn-without-hover { + &:hover { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + } + } + + .icon-btn { + &:hover { + background-color: transparent !important; + color: $primaryHeaderColor !important; + border: 0; + } + } + + .danger-icon-btn { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + + &:hover { + background-color: transparent !important; + color: $primaryErrorColor !important; + border: 0; + } + + .pi { + font-size: 1.275rem !important; + } + } + + .danger-btn { + background-color: $primaryErrorColor !important; + color: $primaryErrorColor !important; + border: 0 !important; + + &:hover { + background-color: $primaryErrorColor !important; + color: $primaryTextColor !important; + border: 0; + } + + .pi { + font-size: 1.275rem !important; + } + } + + .p-datatable .p-sortable-column.p-highlight, + .p-datatable .p-sortable-column.p-highlight .p-sortable-column-icon { + color: $primaryHeaderColor !important; + } + + .p-dropdown:not(.p-disabled):hover, + .p-dropdown:not(.p-disabled).p-focus, + .p-link:focus { + border-color: $primaryHeaderColor !important; + box-shadow: none !important; + } + + .p-paginator .p-paginator-pages .p-paginator-page.p-highlight { + background: transparent !important; + background-color: transparent !important; + color: $primaryHeaderColor !important; + } + + .p-paginator .p-paginator-pages .p-paginator-page:not(.p-highlight):hover, + .p-paginator .p-paginator-first:not(.p-disabled):not(.p-highlight):hover, + .p-paginator .p-paginator-prev:not(.p-disabled):not(.p-highlight):hover, + .p-paginator .p-paginator-next:not(.p-disabled):not(.p-highlight):hover, + .p-paginator .p-paginator-last:not(.p-disabled):not(.p-highlight):hover { + background-color: $primaryBackgroundColor !important; + color: $primaryHeaderColor !important; + } +} diff --git a/kdb-web/src/styles/themes/sh-edraft-light-theme.scss b/kdb-web/src/styles/themes/sh-edraft-light-theme.scss new file mode 100644 index 00000000..fdd80949 --- /dev/null +++ b/kdb-web/src/styles/themes/sh-edraft-light-theme.scss @@ -0,0 +1,515 @@ +.sh-edraft-light-theme { + $primaryTextColor: #272727; + $secondayTextColor: #fff; + $secondayTextColor2: #ef9d0d; + + $primaryHeaderColor: #ef9d0d; + $secondaryHeaderColor: #ffce4c; + $secondaryHeaderColor2: #b76f00; + + $primaryBackgroundColor: #fff; + $secondaryBackgroundColor: #cccccc; + $secondaryBackgroundColor2: #4f4f4f; + $secondaryBackgroundColor3: #272727; + + $primaryErrorColor: #b00020; + $secondaryErrorColor: #e94948; + + $default-border: 1px solid $secondaryBackgroundColor; + + html, + body { + margin: 0; + + font-size: 16px; + } + + h1, + h2 { + color: $primaryHeaderColor; + } + + input, + p { + color: $primaryTextColor; + } + + input { + background-color: $primaryBackgroundColor !important; + } + + .input-field-info-text { + color: $primaryTextColor; + } + + /* Change Autocomplete styles in Chrome*/ + input:-webkit-autofill, + input:-webkit-autofill:hover, + input:-webkit-autofill:focus, + textarea:-webkit-autofill, + textarea:-webkit-autofill:hover, + textarea:-webkit-autofill:focus, + select:-webkit-autofill, + select:-webkit-autofill:hover, + select:-webkit-autofill:focus { + border: 1px solid $primaryHeaderColor; + -webkit-text-fill-color: $primaryTextColor; + -webkit-box-shadow: 0 0 0px 1000px $primaryBackgroundColor inset; + transition: background-color 5000s ease-in-out 0s; + } + + header { + background-color: $primaryBackgroundColor; + + .logo-button-wrapper { + .p-button.p-button-text { + color: $primaryTextColor; + + &:hover { + color: $primaryTextColor; + background-color: $primaryBackgroundColor; + } + } + } + + .logo { + color: $primaryHeaderColor; + } + } + + h1 { + color: $primaryHeaderColor; + } + + .app { + .sidebar { + background-color: $primaryBackgroundColor; + + .menu { + color: $primaryTextColor; + } + } + + .component-wrapper { + color: $primaryTextColor; + + .component { + background-color: $secondaryBackgroundColor; + + .content-wrapper { + background-color: $primaryBackgroundColor; + border-top: 2px solid $primaryHeaderColor; + + .content-header { + border-bottom: $default-border; + } + + .content { + .content-row { + } + + .content-column { + } + + .content-data-name { + } + + .content-data-value { + } + + .content-divider { + border-bottom: $default-border; + } + } + } + } + } + } + + .p-dialog-header { + background-color: $secondaryBackgroundColor !important; + color: $primaryTextColor !important; + } + + .p-dialog-content { + .content-data-name, + .content-data-value { + color: $primaryTextColor; + + .text-btn { + font-size: 18px !important; + font-weight: 400 !important; + } + } + } + + footer { + background-color: $primaryBackgroundColor; + color: $primaryTextColor; + + a { + color: $primaryTextColor; + } + } + + .invalid-feedback { + color: $primaryErrorColor; + } + + .invalid-feedback-input, + .invalid-feedback-input:focus, + .invalid-feedback-input:hover { + input, + input:enabled:focus { + outline: 1px solid $primaryErrorColor !important; + border: 1px solid $primaryErrorColor !important; + border-color: $primaryErrorColor !important; + } + } + + .login-wrapper { + background-color: $secondaryBackgroundColor; + + .login-form-wrapper { + background-color: $primaryBackgroundColor; + + .login-form { + .input-field { + input, + .p-password { + } + } + + .login-form-submit { + .login-form-submit-btn { + } + } + + .login-form-sub-button-wrapper { + .login-form-sub-btn { + background-color: $primaryBackgroundColor; + color: $secondayTextColor2; + border: 2px solid $secondayTextColor2; + + &:hover { + background-color: $primaryHeaderColor !important; + color: $primaryTextColor !important; + } + } + + .login-form-sub-login-btn { + border: none; + } + } + } + } + } + + .spinner-component-wrapper { + background-color: rgba($secondaryBackgroundColor, 0.5); + + .spinner-wrapper { + .custom-spinner .p-progress-spinner-circle { + color: $primaryHeaderColor; + } + } + } + + .wrapper-right { + justify-content: flex-end !important; + } + + /* + PrimeNG Fixes + */ + .p-progress-spinner-circle { + stroke: $primaryHeaderColor !important; + } + + .p-menu { + color: $primaryTextColor !important; + + .p-menuitem-link .p-menuitem-text, + .p-menuitem-link .p-menuitem-icon { + color: $primaryTextColor !important; + } + + .p-menuitem-link:focus { + box-shadow: none !important; + } + + .p-menuitem-link:hover { + background-color: $secondaryBackgroundColor !important; + $border-radius: 20px; + border-radius: $border-radius 0px 0px $border-radius; + + .p-menuitem-text, + .p-menuitem-icon { + color: $primaryHeaderColor !important; + } + } + } + + .p-menu-overlay { + background-color: $primaryBackgroundColor !important; + color: $primaryTextColor !important; + border-top: 2px solid $primaryHeaderColor !important; + + .p-menuitem-link:hover { + background-color: $primaryBackgroundColor !important; + .p-menuitem-text, + .p-menuitem-icon { + color: $primaryHeaderColor !important; + } + } + } + + p-dropdown { + .p-dropdown { + background-color: $primaryBackgroundColor !important; + border-color: $primaryTextColor !important; + color: $primaryTextColor !important; + + span { + color: $primaryTextColor; + } + + .p-dropdown-panel { + background-color: $primaryBackgroundColor !important; + + .p-dropdown-items .p-dropdown-item:not(.p-highlight):not(.p-disabled):hover { + background-color: $primaryBackgroundColor !important; + color: $primaryHeaderColor !important; + + span { + color: $primaryHeaderColor !important; + } + } + + .p-dropdown-items .p-dropdown-item.p-highlight { + background-color: $primaryBackgroundColor !important; + color: $primaryHeaderColor !important; + + span { + color: $primaryHeaderColor !important; + } + } + } + } + } + + p-table { + background-color: $primaryBackgroundColor; + color: $primaryTextColor !important; + + .table-caption { + .table-caption-text { + } + + .table-caption-search-wrapper { + .table-caption-search { + height: 30px !important; + + .table-caption-search-icon { + } + + .table-caption-search-input { + height: 100% !important; + + &:focus { + outline-color: $primaryHeaderColor; + } + } + } + } + + .table-caption-btn-wrapper { + height: 30px !important; + } + } + + .table-edit-input { + width: 100% !important; + } + } + + p-checkbox { + .p-checkbox .p-checkbox-box { + background: $primaryBackgroundColor !important; + background-color: $primaryBackgroundColor !important; + } + + .p-checkbox .p-checkbox-box.p-highlight { + background: $primaryBackgroundColor !important; + background-color: $primaryBackgroundColor !important; + border-color: $primaryTextColor !important; + box-shadow: none !important; + } + + .p-checkbox .p-checkbox-box .p-checkbox-icon { + color: $primaryTextColor !important; + } + + .p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box.p-focus, + .p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box:hover { + border-color: $primaryHeaderColor !important; + box-shadow: none !important; + + .p-checkbox-icon { + color: $primaryHeaderColor !important; + } + } + } + + p-dialog, + p-confirmdialog, + p-dynamicdialog { + .p-dialog.p-confirm-dialog .p-confirm-dialog-message { + margin-left: 0px !important; + } + + .p-dialog { + background-color: $primaryBackgroundColor !important; + + .p-dialog-header { + background-color: $secondaryBackgroundColor !important; + color: $primaryTextColor !important; + } + + .p-dialog-content, + .p-dialog-footer { + background-color: $primaryBackgroundColor !important; + color: $primaryTextColor !important; + } + } + } + + input, + .p-password { + &:focus { + box-shadow: none !important; + } + + &:hover, + &:active, + &:enabled:focus { + border-color: $primaryHeaderColor !important; + } + } + + .btn-wrapper { + display: flex !important; + align-items: center !important; + } + + .btn { + background-color: $primaryHeaderColor; + color: $primaryTextColor; + border: 0; + + &:hover, + &:enabled:hover { + background-color: $primaryHeaderColor; + color: $primaryTextColor; + border: 0; + } + } + + .icon-btn { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + + .pi { + font-size: 1.275rem !important; + } + } + + .text-btn { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + padding: 0px !important; + + &:hover { + background-color: transparent !important; + color: $primaryHeaderColor !important; + border: 0; + } + } + + .icon-btn-without-hover { + &:hover { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + } + } + + .icon-btn { + &:hover { + background-color: transparent !important; + color: $primaryHeaderColor !important; + border: 0; + } + } + + .danger-icon-btn { + background-color: transparent !important; + color: $primaryTextColor !important; + border: 0 !important; + + &:hover { + background-color: transparent !important; + color: $primaryErrorColor !important; + border: 0; + } + + .pi { + font-size: 1.275rem !important; + } + } + + .danger-btn { + background-color: $primaryErrorColor !important; + color: $primaryErrorColor !important; + border: 0 !important; + + &:hover { + background-color: $primaryErrorColor !important; + color: $primaryTextColor !important; + border: 0; + } + + .pi { + font-size: 1.275rem !important; + } + } + + .p-datatable .p-sortable-column.p-highlight, + .p-datatable .p-sortable-column.p-highlight .p-sortable-column-icon { + color: $primaryHeaderColor !important; + } + + .p-dropdown:not(.p-disabled):hover, + .p-dropdown:not(.p-disabled).p-focus, + .p-link:focus { + border-color: $primaryHeaderColor !important; + box-shadow: none !important; + } + + .p-paginator .p-paginator-pages .p-paginator-page.p-highlight { + background: transparent !important; + background-color: transparent !important; + color: $primaryHeaderColor !important; + } + + .p-paginator .p-paginator-pages .p-paginator-page:not(.p-highlight):hover, + .p-paginator .p-paginator-first:not(.p-disabled):not(.p-highlight):hover, + .p-paginator .p-paginator-prev:not(.p-disabled):not(.p-highlight):hover, + .p-paginator .p-paginator-next:not(.p-disabled):not(.p-highlight):hover, + .p-paginator .p-paginator-last:not(.p-disabled):not(.p-highlight):hover { + background-color: $primaryBackgroundColor !important; + color: $primaryHeaderColor !important; + } +} diff --git a/kdb-web/src/test.ts b/kdb-web/src/test.ts new file mode 100644 index 00000000..c04c8760 --- /dev/null +++ b/kdb-web/src/test.ts @@ -0,0 +1,26 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context(path: string, deep?: boolean, filter?: RegExp): { + (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); diff --git a/kdb-web/tsconfig.app.json b/kdb-web/tsconfig.app.json new file mode 100644 index 00000000..82d91dc4 --- /dev/null +++ b/kdb-web/tsconfig.app.json @@ -0,0 +1,15 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts", + "src/polyfills.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/kdb-web/tsconfig.json b/kdb-web/tsconfig.json new file mode 100644 index 00000000..ff06eae1 --- /dev/null +++ b/kdb-web/tsconfig.json @@ -0,0 +1,32 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist/out-tsc", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "sourceMap": true, + "declaration": false, + "downlevelIteration": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "importHelpers": true, + "target": "es2020", + "module": "es2020", + "lib": [ + "es2020", + "dom" + ] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/kdb-web/tsconfig.spec.json b/kdb-web/tsconfig.spec.json new file mode 100644 index 00000000..092345b0 --- /dev/null +++ b/kdb-web/tsconfig.spec.json @@ -0,0 +1,18 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts", + "src/polyfills.ts" + ], + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/kdb-web/update-version.ts b/kdb-web/update-version.ts new file mode 100644 index 00000000..9131c499 --- /dev/null +++ b/kdb-web/update-version.ts @@ -0,0 +1,54 @@ +import { Appsettings } from 'src/app/models/config/appsettings'; +import { SoftwareVersion } from './src/app/models/config/software-version'; + +const jsonFilePath = './src/assets/config.json'; + +function Main(): void { + getVersion() + .then(version => { + setVersion(version); + }) + .catch(err => { + throw err; + }); +} + +async function getVersion(): Promise { + const util = require('util'); + const exec = util.promisify(require('child_process').exec); + + let major = '0'; + let minor = '0'; + let micro = '0'; + + const branch: string = (await exec('git rev-parse --abbrev-ref HEAD')).stdout.toString().trim(); + if (branch.includes('.')) { + const versions = branch.split('.'); + if (versions.length > 0) { + major = versions[0]; + } + + if (versions.length > 1) { + minor = versions[1]; + } + + if (versions.length > 2) { + micro = versions[2]; + } + } + return new SoftwareVersion(major, minor, micro); +} + +async function setVersion(version: SoftwareVersion) { + var fs = require('fs'); + fs.readFile(jsonFilePath, 'utf8', (err: Error, data: string) => { + if (err) { + throw err; + } + const settings: Appsettings = JSON.parse(data); + settings.WebVersion = version; + fs.writeFile(jsonFilePath, JSON.stringify(settings, null, 4), 'utf8', () => {}); + }); +} + +Main(); From 651482a1b98ee09bc07cd9164aa075a4fd88a110 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 01:55:20 +0200 Subject: [PATCH 026/275] Added api connection check #70 --- kdb-bot/src/bot/bot.json | 4 +- kdb-bot/src/bot_api/api.py | 25 +++- kdb-web/package-lock.json | 107 ++++++++++++++---- kdb-web/package.json | 2 + kdb-web/src/app/app.component.ts | 3 + kdb-web/src/app/app.module.ts | 4 +- .../services/socket/socket.service.spec.ts | 16 +++ .../src/app/services/socket/socket.service.ts | 71 ++++++++++++ kdb-web/src/assets/config.json | 2 +- 9 files changed, 210 insertions(+), 24 deletions(-) create mode 100644 kdb-web/src/app/services/socket/socket.service.spec.ts create mode 100644 kdb-web/src/app/services/socket/socket.service.ts diff --git a/kdb-bot/src/bot/bot.json b/kdb-bot/src/bot/bot.json index 799d3fe8..985c978b 100644 --- a/kdb-bot/src/bot/bot.json +++ b/kdb-bot/src/bot/bot.json @@ -24,7 +24,9 @@ "Flask-Classful==0.14.2", "Flask-Cors==3.0.10", "PyJWT==2.5.0", - "waitress==2.1.2" + "waitress==2.1.2", + "Flask-SocketIO==5.3.1", + "eventlet==0.33.1" ], "DevDependencies": [ "cpl-cli==2022.10.0" diff --git a/kdb-bot/src/bot_api/api.py b/kdb-bot/src/bot_api/api.py index 7930b861..b79a12d2 100644 --- a/kdb-bot/src/bot_api/api.py +++ b/kdb-bot/src/bot_api/api.py @@ -3,11 +3,15 @@ import sys import uuid from functools import partial +import eventlet from cpl_core.dependency_injection import ServiceProviderABC +from eventlet import wsgi from flask import Flask, request, jsonify, Response, make_response from flask_cors import CORS +from flask_socketio import SocketIO from bot_api.configuration.api_settings import ApiSettings +from bot_api.configuration.frontend_settings import FrontendSettings from bot_api.exception.service_exception import ServiceException from bot_api.logging.api_logger import ApiLogger from bot_api.model.error_dto import ErrorDTO @@ -21,6 +25,7 @@ class Api(Flask): logger: ApiLogger, services: ServiceProviderABC, api_settings: ApiSettings, + frontend_settings: FrontendSettings, *args, **kwargs ): if not args: @@ -39,6 +44,11 @@ class Api(Flask): exc_class, code = self._get_exc_class_and_code(Exception) self.error_handler_spec[None][code][exc_class] = self.handle_exception + # websockets + self._socketio = SocketIO(self, cors_allowed_origins='*') + self._socketio.on_event('connect', self.on_connect) + self._socketio.on_event('disconnect', self.on_disconnect) + def _register_routes(self): for path, f in Route.registered_routes.items(): route = f[0] @@ -76,6 +86,17 @@ class Api(Flask): def start(self): self._logger.info(__name__, f'Starting API {self._apt_settings.host}:{self._apt_settings.port}') self._register_routes() - from waitress import serve + # from waitress import serve # https://docs.pylonsproject.org/projects/waitress/en/stable/arguments.html - serve(self, host=self._apt_settings.host, port=self._apt_settings.port, threads=10, connection_limit=1000, channel_timeout=10) + # serve(self, host=self._apt_settings.host, port=self._apt_settings.port, threads=10, connection_limit=1000, channel_timeout=10) + wsgi.server( + eventlet.listen((self._apt_settings.host, self._apt_settings.port)), + self, + log_output=False + ) + + def on_connect(self): + self._logger.info(__name__, f'Client connected') + + def on_disconnect(self): + self._logger.info(__name__, f'Client disconnected') diff --git a/kdb-web/package-lock.json b/kdb-web/package-lock.json index 46d080cf..273efa03 100644 --- a/kdb-web/package-lock.json +++ b/kdb-web/package-lock.json @@ -1,12 +1,12 @@ { "name": "kdb-web", - "version": "0.0.0", + "version": "0.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "kdb-web", - "version": "0.0.0", + "version": "0.3.0", "dependencies": { "@angular/animations": "^14.0.0", "@angular/common": "^14.0.0", @@ -20,9 +20,11 @@ "@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", "rxjs": "~7.5.0", + "socket.io-client": "^4.5.3", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -3080,8 +3082,7 @@ "node_modules/@socket.io/component-emitter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, "node_modules/@tootallnate/once": { "version": "1.1.2", @@ -3267,6 +3268,15 @@ "@types/node": "*" } }, + "node_modules/@types/socket.io-client": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-3.0.0.tgz", + "integrity": "sha512-s+IPvFoEIjKA3RdJz/Z2dGR4gLgysKi8owcnrVwNjgvc01Lk68LJDDsG2GRqegFITcxmvCMYM7bhMpwEMlHmDg==", + "deprecated": "This is a stub types definition. socket.io-client provides its own type definitions, so you do not need this installed.", + "dependencies": { + "socket.io-client": "*" + } + }, "node_modules/@types/sockjs": { "version": "0.3.33", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", @@ -4902,7 +4912,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -5194,11 +5203,22 @@ "node": ">=10.0.0" } }, + "node_modules/engine.io-client": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.3.tgz", + "integrity": "sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0" + } + }, "node_modules/engine.io-parser": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", - "dev": true, "engines": { "node": ">=10.0.0" } @@ -8305,8 +8325,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -11018,11 +11037,24 @@ "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", "dev": true }, + "node_modules/socket.io-client": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.3.tgz", + "integrity": "sha512-I/hqDYpQ6JKwtJOf5ikM+Qz+YujZPMEl6qBLhxiP0nX+TfXKhW4KZZG8lamrD6Y5ngjmYHreESVasVCgi5Kl3A==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.2.3", + "socket.io-parser": "~4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/socket.io-parser": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", - "dev": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -12416,7 +12448,6 @@ "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "dev": true, "engines": { "node": ">=10.0.0" }, @@ -12433,6 +12464,14 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -14566,8 +14605,7 @@ "@socket.io/component-emitter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, "@tootallnate/once": { "version": "1.1.2", @@ -14750,6 +14788,14 @@ "@types/node": "*" } }, + "@types/socket.io-client": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-3.0.0.tgz", + "integrity": "sha512-s+IPvFoEIjKA3RdJz/Z2dGR4gLgysKi8owcnrVwNjgvc01Lk68LJDDsG2GRqegFITcxmvCMYM7bhMpwEMlHmDg==", + "requires": { + "socket.io-client": "*" + } + }, "@types/sockjs": { "version": "0.3.33", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", @@ -16001,7 +16047,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -16223,11 +16268,22 @@ "ws": "~8.2.3" } }, + "engine.io-client": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.3.tgz", + "integrity": "sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0" + } + }, "engine.io-parser": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", - "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", - "dev": true + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==" }, "enhanced-resolve": { "version": "5.10.0", @@ -18475,8 +18531,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "multicast-dns": { "version": "7.2.5", @@ -20407,11 +20462,21 @@ "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", "dev": true }, + "socket.io-client": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.3.tgz", + "integrity": "sha512-I/hqDYpQ6JKwtJOf5ikM+Qz+YujZPMEl6qBLhxiP0nX+TfXKhW4KZZG8lamrD6Y5ngjmYHreESVasVCgi5Kl3A==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.2.3", + "socket.io-parser": "~4.2.0" + } + }, "socket.io-parser": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", - "dev": true, "requires": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -21424,9 +21489,13 @@ "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "dev": true, "requires": {} }, + "xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==" + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/kdb-web/package.json b/kdb-web/package.json index 5d0195d0..e3824696 100644 --- a/kdb-web/package.json +++ b/kdb-web/package.json @@ -25,9 +25,11 @@ "@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", "rxjs": "~7.5.0", + "socket.io-client": "^4.5.3", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/kdb-web/src/app/app.component.ts b/kdb-web/src/app/app.component.ts index 90758e7d..6d8046ff 100644 --- a/kdb-web/src/app/app.component.ts +++ b/kdb-web/src/app/app.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { AuthService } from './services/auth/auth.service'; +import { SocketService } from './services/socket/socket.service'; import { ThemeService } from './services/theme/theme.service'; @Component({ @@ -12,9 +13,11 @@ export class AppComponent implements OnInit { constructor( public authService: AuthService, public themeService: ThemeService, + private socket: SocketService ) { } ngOnInit(): void { + this.socket.startSocket(); this.themeService.loadTheme(); this.themeService.loadMenu(); } diff --git a/kdb-web/src/app/app.module.ts b/kdb-web/src/app/app.module.ts index c8ae9aed..bda8098f 100644 --- a/kdb-web/src/app/app.module.ts +++ b/kdb-web/src/app/app.module.ts @@ -9,6 +9,7 @@ import { ConfirmationService, MessageService } from 'primeng/api'; import { DialogService } from 'primeng/dynamicdialog'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; +import { NotFoundComponent } from './components/error/not-found/not-found.component'; import { FooterComponent } from './components/footer/footer.component'; import { HeaderComponent } from './components/header/header.component'; import { SidebarComponent } from './components/sidebar/sidebar.component'; @@ -16,7 +17,8 @@ import { SpinnerComponent } from './components/spinner/spinner.component'; import { SharedModule } from './modules/shared/shared.module'; import { ErrorHandlerService } from './services/error-handler/error-handler.service'; import { SettingsService } from './services/settings/settings.service'; -import { NotFoundComponent } from './components/error/not-found/not-found.component'; + + @NgModule({ declarations: [ diff --git a/kdb-web/src/app/services/socket/socket.service.spec.ts b/kdb-web/src/app/services/socket/socket.service.spec.ts new file mode 100644 index 00000000..7ceaf598 --- /dev/null +++ b/kdb-web/src/app/services/socket/socket.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { SocketService } from './socket.service'; + +describe('SocketService', () => { + let service: SocketService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(SocketService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/services/socket/socket.service.ts b/kdb-web/src/app/services/socket/socket.service.ts new file mode 100644 index 00000000..44935874 --- /dev/null +++ b/kdb-web/src/app/services/socket/socket.service.ts @@ -0,0 +1,71 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { ToastOptions } from 'src/app/models/utils/toast-options'; +import { SettingsService } from '../settings/settings.service'; +import { SpinnerService } from '../spinner/spinner.service'; +import { ToastService } from '../toast/toast.service'; +import io from "socket.io-client"; +import { MessageService } from 'primeng/api'; + +@Injectable({ + providedIn: 'root' +}) +export class SocketService { + private socket: any; + disconnected = false; + + constructor( + private settingsService: SettingsService, + private toastService: ToastService, + private spinnerService: SpinnerService, + private messageService: MessageService, + ) { + this.socket = io(`${settingsService.getApiURL()}`) + } + + startSocket() { + this.socket.on('connect', () => { + if (this.disconnected) { + if (this.spinnerService.showSpinnerState) { + this.spinnerService.hideSpinner(); + const options: ToastOptions = { + closable: false + }; + this.messageService.clear(); + this.toastService.info("Server verbunden", "Die Verbindung zum Server konnte hergestellt werden.", options); + } + } + + this.disconnected = false; + console.log('Connected!') + }); + + this.socket.on('connect_error', (err: Error) => { + if (this.disconnected) { + this.spinnerService.showSpinner(); + return; + } + + this.disconnected = true; + + const options: ToastOptions = { + sticky: true, + closable: false + }; + this.messageService.clear(); + this.toastService.error("Server nicht erreichbar", "Die Verbindung zum Server konnte nicht hergestellt werden!\nLaden Sie die Seite neu.", options); + console.error(err.toString()); + }); + + this.socket.on('disconnect', () => { + console.log('Disconnected!'); + const options: ToastOptions = { + sticky: true, + closable: false + }; + this.spinnerService.showSpinner(); + this.messageService.clear(); + this.toastService.error("Verbindung unterbrochen", "Die Verbindung zum Server konnte nicht hergestellt werden!\nLaden Sie die Seite neu.", options); + }); + } +} diff --git a/kdb-web/src/assets/config.json b/kdb-web/src/assets/config.json index 905b2d2f..f1353416 100644 --- a/kdb-web/src/assets/config.json +++ b/kdb-web/src/assets/config.json @@ -2,7 +2,7 @@ "ApiURL": "http://localhost:5000", "WebVersion": { "Major": "0", - "Minor": "3", + "Minor": "0", "Micro": "0" }, "Themes": [ From 3fe8e1503c858b5193aac816749490d15388a528 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 10:11:21 +0200 Subject: [PATCH 027/275] Added authorize check to controller #70 --- kdb-bot/src/bot/main.py | 2 + kdb-bot/src/bot_api/abc/auth_service_abc.py | 10 +++++ kdb-bot/src/bot_api/app_api_extension.py | 26 +++++++++++++ .../src/bot_api/controller/auth_controller.py | 10 +++++ .../src/bot_api/controller/gui_controller.py | 2 + kdb-bot/src/bot_api/route/route.py | 39 +++++++++++++++++++ kdb-bot/src/bot_api/service/auth_service.py | 34 +++++++++++----- 7 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 kdb-bot/src/bot_api/app_api_extension.py diff --git a/kdb-bot/src/bot/main.py b/kdb-bot/src/bot/main.py index d2bbdb5a..9747634b 100644 --- a/kdb-bot/src/bot/main.py +++ b/kdb-bot/src/bot/main.py @@ -11,6 +11,7 @@ from bot.startup_discord_extension import StartupDiscordExtension from bot.startup_migration_extension import StartupMigrationExtension from bot.startup_module_extension import StartupModuleExtension from bot.startup_settings_extension import StartupSettingsExtension +from bot_api.app_api_extension import AppApiExtension from modules.boot_log.boot_log_extension import BootLogExtension from modules.database.database_extension import DatabaseExtension @@ -29,6 +30,7 @@ class Program: .use_extension(StartupMigrationExtension) \ .use_extension(BootLogExtension) \ .use_extension(DatabaseExtension) \ + .use_extension(AppApiExtension) \ .use_startup(Startup) self.app: Application = await app_builder.build_async() await self.app.run_async() diff --git a/kdb-bot/src/bot_api/abc/auth_service_abc.py b/kdb-bot/src/bot_api/abc/auth_service_abc.py index 28d13749..3a006a0d 100644 --- a/kdb-bot/src/bot_api/abc/auth_service_abc.py +++ b/kdb-bot/src/bot_api/abc/auth_service_abc.py @@ -9,6 +9,7 @@ from bot_api.model.email_string_dto import EMailStringDTO from bot_api.model.reset_password_dto import ResetPasswordDTO from bot_api.model.token_dto import TokenDTO from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO +from bot_data.model.auth_user import AuthUser class AuthServiceABC(ABC): @@ -16,6 +17,12 @@ class AuthServiceABC(ABC): @abstractmethod def __init__(self): pass + @abstractmethod + def generate_token(self, user: AuthUser) -> str: pass + + @abstractmethod + def decode_token(self, token: str) -> dict: pass + @abstractmethod async def get_all_auth_users_async(self) -> List[AuthUserDTO]: pass @@ -43,6 +50,9 @@ class AuthServiceABC(ABC): @abstractmethod async def delete_auth_user_async(self, user_dto: AuthUserDTO): pass + @abstractmethod + async def verify_login(self, token_str: str) -> bool: pass + @abstractmethod async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: pass diff --git a/kdb-bot/src/bot_api/app_api_extension.py b/kdb-bot/src/bot_api/app_api_extension.py new file mode 100644 index 00000000..c0b5fd12 --- /dev/null +++ b/kdb-bot/src/bot_api/app_api_extension.py @@ -0,0 +1,26 @@ +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): + + def __init__(self): + ApplicationExtensionABC.__init__(self) + + async def run(self, config: ConfigurationABC, services: ServiceProviderABC): + feature_flags: FeatureFlagsSettings = config.get_configuration(FeatureFlagsSettings) + 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) diff --git a/kdb-bot/src/bot_api/controller/auth_controller.py b/kdb-bot/src/bot_api/controller/auth_controller.py index c5e10d1c..ee64bf65 100644 --- a/kdb-bot/src/bot_api/controller/auth_controller.py +++ b/kdb-bot/src/bot_api/controller/auth_controller.py @@ -39,11 +39,13 @@ class AuthController: self._auth_service = auth_service @Route.get(f'{BasePath}/users') + @Route.authorize async def get_all_users(self) -> Response: result = await self._auth_service.get_all_auth_users_async() return jsonify(result.select(lambda x: x.to_dict())) @Route.post(f'{BasePath}/users/get/filtered') + @Route.authorize async def get_filtered_users(self) -> Response: dto: AuthUserSelectCriteria = JSONProcessor.process(AuthUserSelectCriteria, request.get_json(force=True, silent=True)) result = await self._auth_service.get_filtered_auth_users_async(dto) @@ -51,11 +53,13 @@ class AuthController: return jsonify(result.to_dict()) @Route.get(f'{BasePath}/users/get/') + @Route.authorize async def get_user_from_email(self, email: str) -> Response: result = await self._auth_service.get_auth_user_by_email_async(email) return jsonify(result.to_dict()) @Route.get(f'{BasePath}/users/find/') + @Route.authorize async def find_user_from_email(self, email: str) -> Response: result = await self._auth_service.find_auth_user_by_email_async(email) return jsonify(result.to_dict()) @@ -83,36 +87,42 @@ class AuthController: return '', 200 @Route.post(f'{BasePath}/update-user') + @Route.authorize async def update_user(self): dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True)) await self._auth_service.update_user_async(dto) return '', 200 @Route.post(f'{BasePath}/update-user-as-admin') + @Route.authorize async def update_user_as_admin(self): dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True)) await self._auth_service.update_user_async(dto) return '', 200 @Route.post(f'{BasePath}/refresh') + @Route.authorize async def refresh(self) -> Response: dto: TokenDTO = JSONProcessor.process(TokenDTO, request.get_json(force=True, silent=True)) result = await self._auth_service.refresh_async(dto) return jsonify(result.to_dict()) @Route.post(f'{BasePath}/revoke') + @Route.authorize async def revoke(self): dto: TokenDTO = JSONProcessor.process(TokenDTO, request.get_json(force=True, silent=True)) await self._auth_service.revoke_async(dto) return '', 200 @Route.post(f'{BasePath}/delete-user') + @Route.authorize async def delete_user(self): dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True)) await self._auth_service.delete_auth_user_async(dto) return '', 200 @Route.post(f'{BasePath}/delete-user-by-mail/') + @Route.authorize async def delete_user_by_mail(self, email: str): await self._auth_service.delete_auth_user_by_email_async(email) return '', 200 diff --git a/kdb-bot/src/bot_api/controller/gui_controller.py b/kdb-bot/src/bot_api/controller/gui_controller.py index a2fa883a..dccc2099 100644 --- a/kdb-bot/src/bot_api/controller/gui_controller.py +++ b/kdb-bot/src/bot_api/controller/gui_controller.py @@ -40,6 +40,7 @@ class GuiController: return VersionDTO(version.major, version.minor, version.micro).to_dict() @Route.get(f'{BasePath}/settings') + @Route.authorize async def settings(self): # TODO: Authentication import bot_api @@ -61,6 +62,7 @@ class GuiController: ).to_dict() @Route.get(f'{BasePath}/send-test-mail/') + @Route.authorize async def send_test_mail(self, email: str): # TODO: Authentication mail = EMail() diff --git a/kdb-bot/src/bot_api/route/route.py b/kdb-bot/src/bot_api/route/route.py index 74fbbc67..429b24a3 100644 --- a/kdb-bot/src/bot_api/route/route.py +++ b/kdb-bot/src/bot_api/route/route.py @@ -1,9 +1,48 @@ +from functools import wraps +from typing import Optional + +from flask import request from flask_cors import cross_origin +from bot_api.abc.auth_service_abc import AuthServiceABC +from bot_api.exception.service_error_code_enum import ServiceErrorCode +from bot_api.exception.service_exception import ServiceException +from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC + class Route: registered_routes = {} + _auth_users: Optional[AuthUserRepositoryABC] = None + _auth: Optional[AuthServiceABC] = None + + @classmethod + def init_authorize(cls, auth_users: AuthUserRepositoryABC, auth: AuthServiceABC): + cls._auth_users = auth_users + cls._auth = auth + + @classmethod + def authorize(cls, f): + @wraps(f) + async def decorator(*args, **kwargs): + token = None + if 'Authorization' in request.headers: + bearer = request.headers.get('Authorization') + token = bearer.split()[1] + + if not token: + raise ServiceException(ServiceErrorCode.InvalidData, f'Token not set') + + if cls._auth_users is None or cls._auth is None: + raise ServiceException(ServiceErrorCode.InvalidDependencies, f'Authorize is not initialized') + + if not cls._auth.verify_login(token): + raise ServiceException(ServiceErrorCode.InvalidUser, f'Token expired') + + return await f(*args, **kwargs) + + return decorator + @classmethod def route(cls, path=None, **kwargs): # simple decorator for class based views diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index ede6f15f..0a78a9c4 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -70,7 +70,7 @@ class AuthService(AuthServiceABC): return False - def _generate_token(self, user: AuthUser) -> str: + def generate_token(self, user: AuthUser) -> str: token = jwt.encode( payload={ 'user_id': user.id, @@ -85,7 +85,7 @@ class AuthService(AuthServiceABC): return token - def _decode_token(self, token: str) -> dict: + def decode_token(self, token: str) -> dict: return jwt.decode( token, key=self._auth_settings.secret_key, @@ -292,6 +292,21 @@ class AuthService(AuthServiceABC): self._logger.error(__name__, f'Cannot delete user', e) raise ServiceException(ServiceErrorCode.UnableToDelete, f'Cannot delete user by mail {user_dto.email}') + def verify_login(self, token_str: str) -> bool: + try: + token = self.decode_token(token_str) + if token is None or 'email' not in token: + raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') + + user = self._auth_users.find_auth_user_by_email(token['email']) + if user is None: + raise ServiceException(ServiceErrorCode.InvalidData, 'Token expired') + except Exception as e: + self._logger.error(__name__, f'Refreshing token failed', e) + return False + + return True + async def login_async(self, user_dto: AuthUser) -> TokenDTO: if user_dto is None: raise ServiceException(ServiceErrorCode.InvalidData, 'User not set') @@ -304,7 +319,7 @@ class AuthService(AuthServiceABC): if db_user.password != user_dto.password: raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password') - token = self._generate_token(db_user) + token = self.generate_token(db_user) refresh_token = self._create_and_save_refresh_token(db_user) if db_user.forgot_password_id is not None: db_user.forgot_password_id = None @@ -316,16 +331,16 @@ class AuthService(AuthServiceABC): if token_dto is None: raise ServiceException(ServiceErrorCode.InvalidData, f'Token not set') - token = self._decode_token(token_dto.token) - if token is None or 'email' not in token: - raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') - try: + token = self.decode_token(token_dto.token) + if token is None or 'email' not in token: + raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') + user = self._auth_users.get_auth_user_by_email(token['email']) if user is None or user.refresh_token != token_dto.refresh_token or user.refresh_token_expire_time <= datetime.now(): raise ServiceException(ServiceErrorCode.InvalidData, 'Token expired') - return TokenDTO(self._generate_token(user), self._create_and_save_refresh_token(user)) + return TokenDTO(self.generate_token(user), self._create_and_save_refresh_token(user)) except Exception as e: self._logger.error(__name__, f'Refreshing token failed', e) return TokenDTO('', '') @@ -334,8 +349,9 @@ class AuthService(AuthServiceABC): if token_dto is None or token_dto.token is None or token_dto.refresh_token is None: raise ServiceException(ServiceErrorCode.InvalidData, 'Token not set') - token = self._decode_token(token_dto.token) try: + token = self.decode_token(token_dto.token) + user = self._auth_users.get_auth_user_by_email(token['email']) if user is None or user.refresh_token != token_dto.refresh_token or user.refresh_token_expire_time <= datetime.now(): raise ServiceException(ServiceErrorCode.InvalidData, 'Token expired') From ba881aefa8d91ac96dccfc595a71be2a94046db4 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 12:06:18 +0200 Subject: [PATCH 028/275] Added verify-login #70 --- .../src/bot_api/controller/auth_controller.py | 16 +++- .../exception/service_error_code_enum.py | 4 + kdb-bot/src/bot_api/route/route.py | 17 +++-- .../models/error/service-error-code.enum.ts | 3 +- .../auth-user/auth-user.component.ts | 2 +- kdb-web/src/app/services/auth/auth.service.ts | 75 ++++++++++--------- .../error-handler/error-handler.service.ts | 13 +++- .../app/services/settings/settings.service.ts | 2 +- .../src/app/services/theme/theme.service.ts | 8 +- 9 files changed, 93 insertions(+), 47 deletions(-) diff --git a/kdb-bot/src/bot_api/controller/auth_controller.py b/kdb-bot/src/bot_api/controller/auth_controller.py index ee64bf65..5342df92 100644 --- a/kdb-bot/src/bot_api/controller/auth_controller.py +++ b/kdb-bot/src/bot_api/controller/auth_controller.py @@ -6,6 +6,8 @@ from flask import request, jsonify, Response from bot_api.abc.auth_service_abc import AuthServiceABC from bot_api.api import Api +from bot_api.exception.service_error_code_enum import ServiceErrorCode +from bot_api.exception.service_exception import ServiceException from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria from bot_api.json_processor import JSONProcessor from bot_api.logging.api_logger import ApiLogger @@ -76,6 +78,19 @@ class AuthController: result = await self._auth_service.login_async(dto) return jsonify(result.to_dict()) + @Route.get(f'{BasePath}/verify-login') + async def verify_login(self): + token = None + result = False + if 'Authorization' in request.headers: + bearer = request.headers.get('Authorization') + token = bearer.split()[1] + + if token is not None: + result = self._auth_service.verify_login(token) + + return jsonify(result) + @Route.post(f'{BasePath}/forgot-password/') async def forgot_password(self, email: str): await self._auth_service.forgot_password_async(email) @@ -108,7 +123,6 @@ class AuthController: return jsonify(result.to_dict()) @Route.post(f'{BasePath}/revoke') - @Route.authorize async def revoke(self): dto: TokenDTO = JSONProcessor.process(TokenDTO, request.get_json(force=True, silent=True)) await self._auth_service.revoke_async(dto) diff --git a/kdb-bot/src/bot_api/exception/service_error_code_enum.py b/kdb-bot/src/bot_api/exception/service_error_code_enum.py index 24da48ed..7b19aeb1 100644 --- a/kdb-bot/src/bot_api/exception/service_error_code_enum.py +++ b/kdb-bot/src/bot_api/exception/service_error_code_enum.py @@ -1,5 +1,7 @@ from enum import Enum +from werkzeug.exceptions import Unauthorized + class ServiceErrorCode(Enum): @@ -17,3 +19,5 @@ class ServiceErrorCode(Enum): ConnectionFailed = 8 Timeout = 9 MailError = 10 + + Unauthorized = 11 diff --git a/kdb-bot/src/bot_api/route/route.py b/kdb-bot/src/bot_api/route/route.py index 429b24a3..ce8826bc 100644 --- a/kdb-bot/src/bot_api/route/route.py +++ b/kdb-bot/src/bot_api/route/route.py @@ -1,12 +1,13 @@ from functools import wraps from typing import Optional -from flask import request +from flask import request, jsonify from flask_cors import cross_origin from bot_api.abc.auth_service_abc import AuthServiceABC from bot_api.exception.service_error_code_enum import ServiceErrorCode from bot_api.exception.service_exception import ServiceException +from bot_api.model.error_dto import ErrorDTO from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC @@ -30,14 +31,20 @@ class Route: bearer = request.headers.get('Authorization') token = bearer.split()[1] - if not token: - raise ServiceException(ServiceErrorCode.InvalidData, f'Token not set') + if token is None: + ex = ServiceException(ServiceErrorCode.Unauthorized, f'Token not set') + error = ErrorDTO(ex.error_code, ex.message) + return jsonify(error.to_dict()), 401 if cls._auth_users is None or cls._auth is None: - raise ServiceException(ServiceErrorCode.InvalidDependencies, f'Authorize is not initialized') + ex = ServiceException(ServiceErrorCode.Unauthorized, f'Authorize is not initialized') + error = ErrorDTO(ex.error_code, ex.message) + return jsonify(error.to_dict()), 401 if not cls._auth.verify_login(token): - raise ServiceException(ServiceErrorCode.InvalidUser, f'Token expired') + ex = ServiceException(ServiceErrorCode.Unauthorized, f'Token expired') + error = ErrorDTO(ex.error_code, ex.message) + return jsonify(error.to_dict()), 401 return await f(*args, **kwargs) diff --git a/kdb-web/src/app/models/error/service-error-code.enum.ts b/kdb-web/src/app/models/error/service-error-code.enum.ts index 5853b5d8..5e32aaf1 100644 --- a/kdb-web/src/app/models/error/service-error-code.enum.ts +++ b/kdb-web/src/app/models/error/service-error-code.enum.ts @@ -12,5 +12,6 @@ export enum ServiceErrorCode { ConnectionFailed = 8, Timeout = 9, - MailError = 10 + MailError = 10, + Unauthorized = 11 } diff --git a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts index 598b18d4..c972e7f2 100644 --- a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts +++ b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts @@ -136,7 +136,7 @@ export class AuthUserComponent implements OnInit { loadNextPage() { this.authService.getFilteredUsers(this.searchCriterions).pipe(catchError(err => { this.loading = false; - return throwError(err); + return throwError(() => err); })).subscribe(list => { this.totalRecords = list.totalCount; this.users = list.users; diff --git a/kdb-web/src/app/services/auth/auth.service.ts b/kdb-web/src/app/services/auth/auth.service.ts index f388e291..1608c3f4 100644 --- a/kdb-web/src/app/services/auth/auth.service.ts +++ b/kdb-web/src/app/services/auth/auth.service.ts @@ -2,7 +2,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { JwtHelperService } from '@auth0/angular-jwt'; -import { Observable, Subscription } from 'rxjs'; +import { firstValueFrom, Observable, Subscription } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { AdminUpdateUserDTO } from 'src/app/models/auth/admin-update-user.dto'; import { AuthRoles } from 'src/app/models/auth/auth-roles.enum'; @@ -91,6 +91,14 @@ export class AuthService { }); } + verifyLogin(): Observable { + return this.http.get(`${this.appsettings.getApiURL()}/api/auth/verify-login`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + forgotPassword(email: string): Observable { const emailJson = JSON.stringify(email); return this.http.post(`${this.appsettings.getApiURL()}/api/auth/forgot-password`, emailJson, { @@ -141,27 +149,6 @@ export class AuthService { }); } - logout(): Subscription | null { - const token = this.getToken(); - this.isLoggedIn = false; - localStorage.removeItem('jwt'); - localStorage.removeItem('rjwt'); - this.router.navigate(['/auth/login']); - - if (token && token.token && token.refreshToken) { - return this.http.post(`${this.appsettings.getApiURL()}/api/auth/revoke`, token, { - headers: new HttpHeaders({ - 'Content-Type': 'application/json' - }) - }).pipe(catchError((error: any) => { - error.error = null; - throw error; - })).subscribe(); - } - - return null - } - deleteUserByMail(mail: string) { return this.http.post(`${this.appsettings.getApiURL()}/api/auth/delete-user-by-mail/${mail}`, { headers: new HttpHeaders({ @@ -190,39 +177,55 @@ export class AuthService { return this.jwtHelper.decodeToken(this.getToken().token); } - async isUserLoggedInAsync(): Promise { - this.isLoggedIn = await this._isUserLoggedInAsync(); - return this.isLoggedIn; + logout(): Subscription | null { + const token = this.getToken(); + + if (token && token.token && token.refreshToken) { + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/revoke`, token, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }).pipe(catchError((error: any) => { + error.error = null; + throw error; + })).subscribe(() => { + this.isLoggedIn = false; + localStorage.removeItem('jwt'); + localStorage.removeItem('rjwt'); + this.router.navigate(['/auth/login']); + }); + } + + return null } - private async _isUserLoggedInAsync(): Promise { + async isUserLoggedInAsync(): Promise { const token = this.getToken(); + if (!token || !token.refreshToken) { this.isLoggedIn = false; return false; } - if (token.token && !this.jwtHelper.isTokenExpired(token.token)) { + const verifiedLogin = await firstValueFrom(await this.verifyLogin()); + + if (verifiedLogin) { this.isLoggedIn = true; return true; } - if (this.isLoggedIn !== false) { + if (this.isLoggedIn) { this.spinnerService.showSpinner(); - const resfreshedToken = await this.refresh(token) - .pipe(catchError((err: Error) => { - this.logout(); - this.spinnerService.hideSpinner(); - throw err; - })) - .toPromise(); + + const resfreshedToken = await firstValueFrom(await this.refresh(token)); + this.spinnerService.hideSpinner(); if (resfreshedToken) { this.saveToken(resfreshedToken); return true; } } - this.isLoggedIn = false; + return false; } diff --git a/kdb-web/src/app/services/error-handler/error-handler.service.ts b/kdb-web/src/app/services/error-handler/error-handler.service.ts index ac8bb59a..b2ed4f6d 100644 --- a/kdb-web/src/app/services/error-handler/error-handler.service.ts +++ b/kdb-web/src/app/services/error-handler/error-handler.service.ts @@ -2,6 +2,8 @@ import { HttpErrorResponse } from '@angular/common/http'; import { ErrorHandler, Injectable, Injector } from '@angular/core'; import { Observable, throwError } from 'rxjs'; import { ErrorDTO } from 'src/app/models/error/error-dto'; +import { ServiceErrorCode } from 'src/app/models/error/service-error-code.enum'; +import { AuthService } from '../auth/auth.service'; import { ToastService } from '../toast/toast.service'; @Injectable() @@ -17,6 +19,11 @@ export class ErrorHandlerService implements ErrorHandler { let header = 'Fehler'; const errorDto: ErrorDTO = error.error; + if (errorDto.errorCode === ServiceErrorCode.Unauthorized) { + this.injector.get(AuthService).logout(); + return throwError(() => error); + } + if (errorDto.errorCode !== undefined) { header = 'Fehlercode: ' + errorDto.errorCode; } @@ -29,6 +36,10 @@ export class ErrorHandlerService implements ErrorHandler { this.injector.get(ToastService).error(header, message); } - return throwError(error); + + if (error.status === 401) { + this.injector.get(AuthService).logout(); + } + return throwError(() => error); } } diff --git a/kdb-web/src/app/services/settings/settings.service.ts b/kdb-web/src/app/services/settings/settings.service.ts index 17c292d1..ec2b93d7 100644 --- a/kdb-web/src/app/services/settings/settings.service.ts +++ b/kdb-web/src/app/services/settings/settings.service.ts @@ -22,7 +22,7 @@ export class SettingsService { this.http.get('../../assets/config.json') .pipe(catchError(error => { reject(error); - return throwError(error); + return throwError(() => error); })).subscribe(settings => { this.appsettings = settings; resolve(settings); diff --git a/kdb-web/src/app/services/theme/theme.service.ts b/kdb-web/src/app/services/theme/theme.service.ts index 47537679..f83a4510 100644 --- a/kdb-web/src/app/services/theme/theme.service.ts +++ b/kdb-web/src/app/services/theme/theme.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; import { Themes } from 'src/app/models/view/themes.enum'; import { AuthService } from '../auth/auth.service'; @@ -13,9 +14,14 @@ export class ThemeService { isSidebarOpen = false; hasLangChanged = false; + isSidebarOpen$ = new Subject(); + constructor( private authService: AuthService ) { + this.isSidebarOpen$.subscribe(isSidebarOpen => { + this.isSidebarOpen = isSidebarOpen + }); this.loadTheme(); this.loadMenu(); this.themeName = null; @@ -93,6 +99,6 @@ export class ThemeService { setSideWidth($event: any): void { this.sidebarWidth = $event ? '150px' : '50px'; - this.isSidebarOpen = $event; + this.isSidebarOpen$.next($event) } } From 1a3126dfc5b181658489fb35476c1aea52bfd7ec Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 12:57:27 +0200 Subject: [PATCH 029/275] Removed themeService from templates #70 --- kdb-web/src/app/app.component.html | 8 +-- kdb-web/src/app/app.component.ts | 19 +++++- .../app/components/header/header.component.ts | 5 +- .../components/sidebar/sidebar.component.ts | 63 ++++++++++--------- .../modules/shared/guards/auth/auth.guard.ts | 11 ++-- kdb-web/src/app/services/auth/auth.service.ts | 14 +++-- .../src/app/services/theme/theme.service.ts | 27 +++++--- 7 files changed, 91 insertions(+), 56 deletions(-) diff --git a/kdb-web/src/app/app.component.html b/kdb-web/src/app/app.component.html index a9daaf72..94cdd058 100644 --- a/kdb-web/src/app/app.component.html +++ b/kdb-web/src/app/app.component.html @@ -1,11 +1,11 @@ -
+
- +
-
diff --git a/kdb-web/src/app/app.component.ts b/kdb-web/src/app/app.component.ts index 6d8046ff..4bf34152 100644 --- a/kdb-web/src/app/app.component.ts +++ b/kdb-web/src/app/app.component.ts @@ -10,15 +10,32 @@ import { ThemeService } from './services/theme/theme.service'; }) export class AppComponent implements OnInit { + themeName!: string; + sidebarWidth!: string; + + isLoggedIn: boolean = false; + constructor( public authService: AuthService, - public themeService: ThemeService, + private themeService: ThemeService, private socket: SocketService ) { } ngOnInit(): void { + this.themeService.sidebarWidth$.subscribe(value => { + this.sidebarWidth = value; + }); + this.themeService.themeName$.subscribe(value => { + this.themeName = value; + }); + this.socket.startSocket(); this.themeService.loadTheme(); this.themeService.loadMenu(); } + + + setSideWidth($event: any): void { + this.themeService.setSideWidth($event); + } } diff --git a/kdb-web/src/app/components/header/header.component.ts b/kdb-web/src/app/components/header/header.component.ts index 6d4ad0ee..ff71d92f 100644 --- a/kdb-web/src/app/components/header/header.component.ts +++ b/kdb-web/src/app/components/header/header.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { LangChangeEvent, TranslateService } from '@ngx-translate/core'; import { MenuItem, PrimeNGConfig } from 'primeng/api'; @@ -14,8 +14,6 @@ import { ThemeService } from 'src/app/services/theme/theme.service'; styleUrls: ['./header.component.scss'] }) export class HeaderComponent implements OnInit { - @Output() isSidebarFullWidth: EventEmitter = new EventEmitter(this.themeService.isSidebarOpen); - langList: MenuItem[] = []; themeList: MenuItem[] = []; userMenuList!: MenuItem[]; @@ -112,7 +110,6 @@ export class HeaderComponent implements OnInit { toggleMenu(): void { this.themeService.setIsMenuOpen(!this.themeService.isSidebarOpen); - this.isSidebarFullWidth.emit(this.themeService.isSidebarOpen); } changeTheme(name: string): void { diff --git a/kdb-web/src/app/components/sidebar/sidebar.component.ts b/kdb-web/src/app/components/sidebar/sidebar.component.ts index a87eb35f..ddc840ea 100644 --- a/kdb-web/src/app/components/sidebar/sidebar.component.ts +++ b/kdb-web/src/app/components/sidebar/sidebar.component.ts @@ -3,50 +3,57 @@ import { LangChangeEvent, TranslateService } from '@ngx-translate/core'; import { MenuItem } from 'primeng/api'; import { AuthRoles } from 'src/app/models/auth/auth-roles.enum'; import { AuthService } from 'src/app/services/auth/auth.service'; +import { ThemeService } from 'src/app/services/theme/theme.service'; @Component({ selector: 'app-sidebar', templateUrl: './sidebar.component.html', styleUrls: ['./sidebar.component.scss'] }) -export class SidebarComponent implements OnInit, OnChanges { +export class SidebarComponent implements OnInit { - @Input() isSidebarOpen!: boolean; + isSidebarOpen: boolean = true; menuItems!: MenuItem[]; constructor( private authService: AuthService, - private translateService: TranslateService + private translateService: TranslateService, + private themeService: ThemeService ) { } ngOnInit(): void { - this.translateService.onLangChange.subscribe(async (event: LangChangeEvent) => { - await this.setMenu(this.isSidebarOpen); + this.themeService.isSidebarOpen$.subscribe(value => { + this.isSidebarOpen = value; + this.setMenu(); + }); + + + this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { + this.setMenu(); + }); + this.setMenu(); + } + + setMenu() { + + this.authService.hasUserPermission(AuthRoles.Admin).then(hasPermission => { + this.menuItems = []; + this.menuItems = [ + { label: this.isSidebarOpen ? this.translateService.instant('sidebar.dashboard') : '', icon: 'pi pi-th-large', routerLink: 'dashboard' }, + ]; + + if (!hasPermission) { + return; + } + + this.menuItems.push( + { separator: true }, + { label: this.isSidebarOpen ? this.translateService.instant('sidebar.config') : '', icon: 'pi pi-cog', routerLink: '/admin/settings' }, + { label: this.isSidebarOpen ? this.translateService.instant('sidebar.auth_user_list') : '', icon: 'pi pi-user-edit', routerLink: '/admin/users' }, + ); + this.menuItems = this.menuItems.slice(); }); } - async setMenu(isSidebarOpen: boolean) { - this.menuItems = []; - this.menuItems = [ - { label: isSidebarOpen ? this.translateService.instant('sidebar.dashboard') : '', icon: 'pi pi-th-large', routerLink: 'dashboard' }, - ]; - - if (await this.authService.hasUserPermission(AuthRoles.Admin)) { - this.menuItems.push( - { separator: true }, - { label: isSidebarOpen ? this.translateService.instant('sidebar.config') : '', icon: 'pi pi-cog', routerLink: '/admin/settings' }, - { label: isSidebarOpen ? this.translateService.instant('sidebar.auth_user_list') : '', icon: 'pi pi-user-edit', routerLink: '/admin/users' }, - ); - this.menuItems = this.menuItems.slice(); - } - } - - async ngOnChanges(changes: SimpleChanges): Promise { - if (!changes) - return; - - await this.setMenu(changes['isSidebarOpen'].currentValue); - } - } diff --git a/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts b/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts index 694dc644..deb1be40 100644 --- a/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts +++ b/kdb-web/src/app/modules/shared/guards/auth/auth.guard.ts @@ -28,10 +28,13 @@ export class AuthGuard implements CanActivate { const role = route.data['role']; if (role) { - if (!await this.authService.hasUserPermission(role)) { - this.router.navigate(['/dashboard']); - return false; - } + this.authService.hasUserPermission(role).then(hasPermission => { + if (!hasPermission) { + this.router.navigate(['/dashboard']); + return false; + } + return true; + }); } return true; } diff --git a/kdb-web/src/app/services/auth/auth.service.ts b/kdb-web/src/app/services/auth/auth.service.ts index 1608c3f4..5c007891 100644 --- a/kdb-web/src/app/services/auth/auth.service.ts +++ b/kdb-web/src/app/services/auth/auth.service.ts @@ -229,12 +229,14 @@ export class AuthService { return false; } - async hasUserPermission(role: AuthRoles): Promise { - if (!role || !await this.isUserLoggedInAsync()) { - return false; - } - const token = this.getDecodedToken(); - return AuthRoles[token['role']] === AuthRoles[role]; + hasUserPermission(role: AuthRoles): Promise { + return this.isUserLoggedInAsync().then(isLoggedIn => { + if (!role || !isLoggedIn) { + return false; + } + const token = this.getDecodedToken(); + return AuthRoles[token['role']] === AuthRoles[role]; + }); } getEMailFromDecodedToken(token: { [key: string]: any }): string | null { diff --git a/kdb-web/src/app/services/theme/theme.service.ts b/kdb-web/src/app/services/theme/theme.service.ts index f83a4510..0d3dae59 100644 --- a/kdb-web/src/app/services/theme/theme.service.ts +++ b/kdb-web/src/app/services/theme/theme.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import { LangChangeEvent, TranslateService } from '@ngx-translate/core'; import { Subject } from 'rxjs'; import { Themes } from 'src/app/models/view/themes.enum'; import { AuthService } from '../auth/auth.service'; @@ -8,23 +9,31 @@ import { AuthService } from '../auth/auth.service'; }) export class ThemeService { - themeName: string | null; + themeName: string = Themes.Default; sidebarWidth = '150px'; isSidebarOpen = false; hasLangChanged = false; + themeName$ = new Subject(); isSidebarOpen$ = new Subject(); + sidebarWidth$ = new Subject(); constructor( private authService: AuthService ) { - this.isSidebarOpen$.subscribe(isSidebarOpen => { - this.isSidebarOpen = isSidebarOpen + this.themeName$.subscribe(themeName => { + this.themeName = themeName; }); + this.isSidebarOpen$.subscribe(isSidebarOpen => { + this.isSidebarOpen = isSidebarOpen; + }); + this.sidebarWidth$.subscribe(sidebarWidth => { + this.sidebarWidth = sidebarWidth; + }); + this.loadTheme(); this.loadMenu(); - this.themeName = null; } loadTheme(): void { @@ -53,7 +62,7 @@ export class ThemeService { this.authService.isUserLoggedInAsync().then(result => { if (!result) { localStorage.setItem(`default_themeName`, Themes.Default); - this.themeName = Themes.Default; + this.themeName$.next(Themes.Default); } const token = this.authService.getDecodedToken(); @@ -62,7 +71,7 @@ export class ThemeService { localStorage.setItem(`${mail}_themeName`, name); } localStorage.setItem(`default_themeName`, name); - this.themeName = name; + this.themeName$.next(name); }); } @@ -97,8 +106,8 @@ export class ThemeService { this.setSideWidth(isMenuOpen); } - setSideWidth($event: any): void { - this.sidebarWidth = $event ? '150px' : '50px'; - this.isSidebarOpen$.next($event) + setSideWidth(isSidebarOpen: boolean): void { + this.sidebarWidth$.next(isSidebarOpen ? '150px' : '50px'); + this.isSidebarOpen$.next(isSidebarOpen); } } From dbbd2b54c46172b9ff655b954c425a18e5ea119e Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 13:09:26 +0200 Subject: [PATCH 030/275] Fixed load menu #70 --- kdb-web/src/app/app.component.ts | 1 - .../app/components/sidebar/sidebar.component.ts | 15 +++++++-------- kdb-web/src/app/services/theme/theme.service.ts | 7 +++---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/kdb-web/src/app/app.component.ts b/kdb-web/src/app/app.component.ts index 4bf34152..22a6a7cd 100644 --- a/kdb-web/src/app/app.component.ts +++ b/kdb-web/src/app/app.component.ts @@ -31,7 +31,6 @@ export class AppComponent implements OnInit { this.socket.startSocket(); this.themeService.loadTheme(); - this.themeService.loadMenu(); } diff --git a/kdb-web/src/app/components/sidebar/sidebar.component.ts b/kdb-web/src/app/components/sidebar/sidebar.component.ts index ddc840ea..cbf13c6f 100644 --- a/kdb-web/src/app/components/sidebar/sidebar.component.ts +++ b/kdb-web/src/app/components/sidebar/sidebar.component.ts @@ -20,33 +20,32 @@ export class SidebarComponent implements OnInit { private authService: AuthService, private translateService: TranslateService, private themeService: ThemeService - ) { } - - ngOnInit(): void { + ) { this.themeService.isSidebarOpen$.subscribe(value => { this.isSidebarOpen = value; this.setMenu(); }); - this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { this.setMenu(); }); - this.setMenu(); + } + + ngOnInit(): void { + this.themeService.loadMenu(); } setMenu() { - this.authService.hasUserPermission(AuthRoles.Admin).then(hasPermission => { this.menuItems = []; this.menuItems = [ { label: this.isSidebarOpen ? this.translateService.instant('sidebar.dashboard') : '', icon: 'pi pi-th-large', routerLink: 'dashboard' }, ]; - + if (!hasPermission) { return; } - + this.menuItems.push( { separator: true }, { label: this.isSidebarOpen ? this.translateService.instant('sidebar.config') : '', icon: 'pi pi-cog', routerLink: '/admin/settings' }, diff --git a/kdb-web/src/app/services/theme/theme.service.ts b/kdb-web/src/app/services/theme/theme.service.ts index 0d3dae59..914ba774 100644 --- a/kdb-web/src/app/services/theme/theme.service.ts +++ b/kdb-web/src/app/services/theme/theme.service.ts @@ -27,13 +27,13 @@ export class ThemeService { }); this.isSidebarOpen$.subscribe(isSidebarOpen => { this.isSidebarOpen = isSidebarOpen; + this.sidebarWidth$.next(isSidebarOpen ? '150px' : '50px'); }); this.sidebarWidth$.subscribe(sidebarWidth => { this.sidebarWidth = sidebarWidth; }); - + this.loadTheme(); - this.loadMenu(); } loadTheme(): void { @@ -87,7 +87,7 @@ export class ThemeService { let isMenuOpen = true; let isMenuOpenStr = localStorage.getItem(`${mail}_isMenuOpen`); if (isMenuOpenStr) { - isMenuOpen = Boolean(isMenuOpenStr); + isMenuOpen = JSON.parse(isMenuOpenStr); } this.setIsMenuOpen(isMenuOpen); @@ -107,7 +107,6 @@ export class ThemeService { } setSideWidth(isSidebarOpen: boolean): void { - this.sidebarWidth$.next(isSidebarOpen ? '150px' : '50px'); this.isSidebarOpen$.next(isSidebarOpen); } } From d5ad5ded883d63f3a0f5be3bd520975b8d571d94 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 14:49:46 +0200 Subject: [PATCH 031/275] Fixed header styling #70 --- kdb-web/src/styles.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-web/src/styles.scss b/kdb-web/src/styles.scss index 8964e79c..ee146861 100644 --- a/kdb-web/src/styles.scss +++ b/kdb-web/src/styles.scss @@ -38,7 +38,6 @@ header { display: flex; align-items: center; justify-content: center; - margin: 0px 5px; .p-button.p-button-text { border: none; @@ -55,6 +54,7 @@ header { text-align: right; flex: 1; justify-content: flex-end; + margin: 0px 5px 0px 0px; } .p-menu-overlay { From f319e89473bac2605445ea2eed3a578a2df92654 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 16:01:37 +0200 Subject: [PATCH 032/275] Fixed login state problems #70 --- kdb-web/src/app/app.component.html | 2 +- kdb-web/src/app/app.component.ts | 7 ++- kdb-web/src/app/services/auth/auth.service.ts | 48 +++++++++++-------- .../src/app/services/socket/socket.service.ts | 2 +- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/kdb-web/src/app/app.component.html b/kdb-web/src/app/app.component.html index 94cdd058..a2dc5b50 100644 --- a/kdb-web/src/app/app.component.html +++ b/kdb-web/src/app/app.component.html @@ -1,5 +1,5 @@
- +
diff --git a/kdb-web/src/app/app.component.ts b/kdb-web/src/app/app.component.ts index 22a6a7cd..72139e66 100644 --- a/kdb-web/src/app/app.component.ts +++ b/kdb-web/src/app/app.component.ts @@ -16,7 +16,7 @@ export class AppComponent implements OnInit { isLoggedIn: boolean = false; constructor( - public authService: AuthService, + private authService: AuthService, private themeService: ThemeService, private socket: SocketService ) { } @@ -28,9 +28,12 @@ export class AppComponent implements OnInit { this.themeService.themeName$.subscribe(value => { this.themeName = value; }); + this.authService.isLoggedIn$.subscribe(value => { + this.isLoggedIn = value; + }); - this.socket.startSocket(); this.themeService.loadTheme(); + this.socket.startSocket(); } diff --git a/kdb-web/src/app/services/auth/auth.service.ts b/kdb-web/src/app/services/auth/auth.service.ts index 5c007891..83a9bf22 100644 --- a/kdb-web/src/app/services/auth/auth.service.ts +++ b/kdb-web/src/app/services/auth/auth.service.ts @@ -2,7 +2,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { JwtHelperService } from '@auth0/angular-jwt'; -import { firstValueFrom, Observable, Subscription } from 'rxjs'; +import { firstValueFrom, Observable, Subject, Subscription } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { AdminUpdateUserDTO } from 'src/app/models/auth/admin-update-user.dto'; import { AuthRoles } from 'src/app/models/auth/auth-roles.enum'; @@ -21,8 +21,8 @@ import { SpinnerService } from '../spinner/spinner.service'; }) export class AuthService { - invalidLogin!: boolean; - isLoggedIn!: boolean; + private isLoggedIn!: boolean; + isLoggedIn$ = new Subject(); constructor( private appsettings: SettingsService, @@ -31,7 +31,9 @@ export class AuthService { private jwtHelper: JwtHelperService, private spinnerService: SpinnerService ) { - this.isUserLoggedInAsync(); + this.isLoggedIn$.subscribe(value => { + this.isLoggedIn = value; + }); } /* data requests */ @@ -187,14 +189,20 @@ export class AuthService { }) }).pipe(catchError((error: any) => { error.error = null; + this.isLoggedIn$.next(false); + localStorage.removeItem('rjwt'); + this.router.navigate(['/auth/login']); throw error; })).subscribe(() => { - this.isLoggedIn = false; + this.isLoggedIn$.next(false); localStorage.removeItem('jwt'); localStorage.removeItem('rjwt'); this.router.navigate(['/auth/login']); }); } + this.isLoggedIn$.next(false); + localStorage.removeItem('rjwt'); + this.router.navigate(['/auth/login']); return null } @@ -203,15 +211,19 @@ export class AuthService { const token = this.getToken(); if (!token || !token.refreshToken) { - this.isLoggedIn = false; + this.isLoggedIn$.next(false); return false; } - const verifiedLogin = await firstValueFrom(await this.verifyLogin()); - - if (verifiedLogin) { - this.isLoggedIn = true; - return true; + try { + const verifiedLogin = await firstValueFrom(this.verifyLogin()); + if (verifiedLogin) { + this.isLoggedIn$.next(true); + return true; + } + } catch (error: unknown) { + this.isLoggedIn$.next(false); + return false; } if (this.isLoggedIn) { @@ -229,14 +241,12 @@ export class AuthService { return false; } - hasUserPermission(role: AuthRoles): Promise { - return this.isUserLoggedInAsync().then(isLoggedIn => { - if (!role || !isLoggedIn) { - return false; - } - const token = this.getDecodedToken(); - return AuthRoles[token['role']] === AuthRoles[role]; - }); + async hasUserPermission(role: AuthRoles): Promise { + if (!role || !await this.isUserLoggedInAsync()) { + return false; + } + const token = this.getDecodedToken(); + return AuthRoles[token['role']] === AuthRoles[role]; } getEMailFromDecodedToken(token: { [key: string]: any }): string | null { diff --git a/kdb-web/src/app/services/socket/socket.service.ts b/kdb-web/src/app/services/socket/socket.service.ts index 44935874..a29bba2c 100644 --- a/kdb-web/src/app/services/socket/socket.service.ts +++ b/kdb-web/src/app/services/socket/socket.service.ts @@ -20,10 +20,10 @@ export class SocketService { private spinnerService: SpinnerService, private messageService: MessageService, ) { - this.socket = io(`${settingsService.getApiURL()}`) } startSocket() { + this.socket = io(`${this.settingsService.getApiURL()}`) this.socket.on('connect', () => { if (this.disconnected) { if (this.spinnerService.showSpinnerState) { From c5f371e0d545acef4275d009bc3dbdd5bbcd1737 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 21:10:20 +0200 Subject: [PATCH 033/275] Fixed update user #70 --- .../components/auth-user/auth-user.component.html | 4 ++-- kdb-web/src/assets/i18n/de.json | 14 +++++++------- kdb-web/src/assets/i18n/en.json | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html index 1cb83a55..9f810bda 100644 --- a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html +++ b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.html @@ -43,10 +43,10 @@
- +
{{'admin.auth_users.headers.e_mail' | translate}}
- +
diff --git a/kdb-web/src/assets/i18n/de.json b/kdb-web/src/assets/i18n/de.json index b51660d4..894bd461 100644 --- a/kdb-web/src/assets/i18n/de.json +++ b/kdb-web/src/assets/i18n/de.json @@ -68,21 +68,21 @@ "no_entries_found": "Keine Einträge gefunden", "message": { "invalid_email": "Ungültige E-Mail", - "invalid_email_d": "Die E-Mail {{eMail}} ist nicht gültig!", + "invalid_email_d": "Die E-Mail {{email}} ist nicht gültig!", "user_already_exists": "Benutzer existiert bereits", - "user_already_exists_d": "Der Benutzer {{eMail}} existiert bereits!", + "user_already_exists_d": "Der Benutzer {{email}} existiert bereits!", "user_added": "Benutzer hinzugefügt", - "user_added_d": "Benutzer {{eMail}} erfolgreich hinzugefügt", + "user_added_d": "Benutzer {{email}} erfolgreich hinzugefügt", "user_change_failed": "Benutzer änderung fehlgeschlagen", - "user_change_failed_d": "Benutzer {{eMail}} konnte nicht geändert werden!", + "user_change_failed_d": "Benutzer {{email}} konnte nicht geändert werden!", "user_changed": "Benutzer geändert", - "user_changed_d": "Benutzer {{eMail}} erfolgreich geändert", + "user_changed_d": "Benutzer {{email}} erfolgreich geändert", "cannot_delete_user": "Benutzer kann nicht gelöscht werden", "logon_with_another_user": "Loggen Sie sich mit einem anderen Benutzer ein, um diesen Benutzer zu löschen!", "user_delete": "Benutzer löschen", - "user_delete_q": "Sind Sie sich sicher, dass Sie {{eMail}} löschen möchten?", + "user_delete_q": "Sind Sie sich sicher, dass Sie {{email}} löschen möchten?", "user_deleted": "Benutzer gelöscht", - "user_deleted_d": "Benutzer {{eMail}} erfolgreich gelöscht" + "user_deleted_d": "Benutzer {{email}} erfolgreich gelöscht" } } }, diff --git a/kdb-web/src/assets/i18n/en.json b/kdb-web/src/assets/i18n/en.json index 28a8c47b..d289c560 100644 --- a/kdb-web/src/assets/i18n/en.json +++ b/kdb-web/src/assets/i18n/en.json @@ -67,21 +67,21 @@ "no_entries_found": "No entries found", "message": { "invalid_email": "Invalid E-Mail", - "invalid_email_d": "The e-mail {{eMail}} is not valid!", + "invalid_email_d": "The e-mail {{email}} is not valid!", "user_already_exists": "User already exists", - "user_already_exists_d": "The user {{eMail}} already exists!", + "user_already_exists_d": "The user {{email}} already exists!", "user_added": "User added", - "user_added_d": "User {{eMail}} successfully added", + "user_added_d": "User {{email}} successfully added", "user_change_failed": "User change failed", - "user_change_failed_d": "User {{eMail}} could not be changed!", + "user_change_failed_d": "User {{email}} could not be changed!", "user_changed": "User changed", - "user_changed_d": "User {{eMail}} changed successfully", + "user_changed_d": "User {{email}} changed successfully", "cannot_delete_user": "User cannot be deleted", "logon_with_another_user": "Log in with another user to delete this user!", "user_delete": "Delete user", - "user_delete_q": "Are you sure you want to delete {{eMail}}?", + "user_delete_q": "Are you sure you want to delete {{email}}?", "user_deleted": "User deleted", - "user_deleted_d": "User {{eMail}} successfully deleted" + "user_deleted_d": "User {{email}} successfully deleted" } } }, From 29fa35dffe1d2383a5b6c8af2e5b7b808bf5da4a Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 21:16:04 +0200 Subject: [PATCH 034/275] Fixed update user #70 --- kdb-bot/src/bot_api/controller/auth_controller.py | 2 +- kdb-bot/src/bot_api/service/auth_service.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/kdb-bot/src/bot_api/controller/auth_controller.py b/kdb-bot/src/bot_api/controller/auth_controller.py index 5342df92..a23d490d 100644 --- a/kdb-bot/src/bot_api/controller/auth_controller.py +++ b/kdb-bot/src/bot_api/controller/auth_controller.py @@ -112,7 +112,7 @@ class AuthController: @Route.authorize async def update_user_as_admin(self): dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True)) - await self._auth_service.update_user_async(dto) + await self._auth_service.update_user_as_admin_async(dto) return '', 200 @Route.post(f'{BasePath}/refresh') diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index 0a78a9c4..e1e4a2b9 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -273,6 +273,7 @@ class AuthService(AuthServiceABC): if user.auth_role == update_user_dto.auth_user.auth_role and user.auth_role != update_user_dto.new_auth_user.auth_role: user.auth_role = update_user_dto.new_auth_user.auth_role + self._auth_users.update_auth_user(user) self._db.save_changes() async def delete_auth_user_by_email_async(self, email: str): From f634ad552e3aa1fe56a35f3e06c6e30018b57965 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 21:29:58 +0200 Subject: [PATCH 035/275] Fixed settings dto #70 --- .../src/bot_api/controller/gui_controller.py | 21 +++++++++++-------- kdb-bot/src/bot_api/model/settings_dto.py | 2 +- kdb-bot/src/bot_api/model/version_dto.py | 16 ++++++++++++++ 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/kdb-bot/src/bot_api/controller/gui_controller.py b/kdb-bot/src/bot_api/controller/gui_controller.py index dccc2099..d956e900 100644 --- a/kdb-bot/src/bot_api/controller/gui_controller.py +++ b/kdb-bot/src/bot_api/controller/gui_controller.py @@ -4,8 +4,10 @@ from cpl_core.configuration import ConfigurationABC from cpl_core.environment import ApplicationEnvironmentABC from cpl_core.mailing import EMail, EMailClientABC, EMailClientSettings from cpl_translation import TranslatePipe +from flask import jsonify from bot_api.api import Api +from bot_api.configuration.authentication_settings import AuthenticationSettings from bot_api.logging.api_logger import ApiLogger from bot_api.model.settings_dto import SettingsDTO from bot_api.model.version_dto import VersionDTO @@ -23,7 +25,8 @@ class GuiController: t: TranslatePipe, api: Api, mail_settings: EMailClientSettings, - mailer: EMailClientABC + mailer: EMailClientABC, + auth_settings: AuthenticationSettings ): self._config = config self._env = env @@ -32,6 +35,7 @@ class GuiController: self._api = api self._mail_settings = mail_settings self._mailer = mailer + self._auth_settings = auth_settings @Route.get(f'{BasePath}/api-version') async def api_version(self): @@ -42,29 +46,27 @@ class GuiController: @Route.get(f'{BasePath}/settings') @Route.authorize async def settings(self): - # TODO: Authentication import bot_api version = bot_api.version_info - return SettingsDTO( + return jsonify(SettingsDTO( '', VersionDTO(version.major, version.minor, version.micro), os.path.abspath(os.path.join(self._env.working_directory, 'config')), - '', '/', - 0, - 0, + '/', + self._auth_settings.token_expire_time, + self._auth_settings.refresh_token_expire_time, self._mail_settings.user_name, self._mail_settings.port, self._mail_settings.host, self._mail_settings.user_name, self._mail_settings.user_name, - ).to_dict() + ).to_dict()) - @Route.get(f'{BasePath}/send-test-mail/') + @Route.post(f'{BasePath}/send-test-mail/') @Route.authorize async def send_test_mail(self, email: str): - # TODO: Authentication mail = EMail() mail.add_header('Mime-Version: 1.0') mail.add_header('Content-Type: text/plain; charset=utf-8') @@ -73,3 +75,4 @@ class GuiController: mail.subject = self._t.transform('api.api.test_mail.subject') mail.body = self._t.transform('api.api.test_mail.message').format(self._env.host_name, self._env.environment_name) self._mailer.send_mail(mail) + return '', 200 diff --git a/kdb-bot/src/bot_api/model/settings_dto.py b/kdb-bot/src/bot_api/model/settings_dto.py index 567f3931..09df375c 100644 --- a/kdb-bot/src/bot_api/model/settings_dto.py +++ b/kdb-bot/src/bot_api/model/settings_dto.py @@ -53,7 +53,7 @@ class SettingsDTO(DtoABC): def to_dict(self) -> dict: return { 'webVersion': self._web_version, - 'apiVersion': self._api_version.to_dict(), + 'apiVersion': self._api_version.str, 'configPath': self._config_path, 'webBaseURL': self._web_base_url, 'apiBaseURL': self._api_base_url, diff --git a/kdb-bot/src/bot_api/model/version_dto.py b/kdb-bot/src/bot_api/model/version_dto.py index c1504478..c15413a0 100644 --- a/kdb-bot/src/bot_api/model/version_dto.py +++ b/kdb-bot/src/bot_api/model/version_dto.py @@ -14,6 +14,22 @@ class VersionDTO(DtoABC): self._minor = minor self._micro = micro + @property + def major(self) -> str: + return self._major + + @property + def minor(self) -> str: + return self._minor + + @property + def micro(self) -> str: + return self._micro + + @property + def str(self) -> str: + return f'{self._major}.{self._minor}.{self._micro}' + def from_dict(self, values: dict): self._major = values['major'] self._minor = values['minor'] From 90bfee23b4ac876bb9dd8cb41f59a465737ac96e Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 21:36:08 +0200 Subject: [PATCH 036/275] Added logic to load api after bot #70 --- kdb-bot/src/bot/application.py | 5 ++--- kdb-bot/src/bot/module_list.py | 2 +- ...h_user_transformer_abc.py => transformer_abc.py} | 2 +- kdb-bot/src/bot_api/api_module.py | 5 +++++ kdb-bot/src/bot_api/event/bot_api_on_ready_event.py | 13 +++++++++++++ .../bot_api/transformer/auth_user_transformer.py | 4 ++-- 6 files changed, 24 insertions(+), 7 deletions(-) rename kdb-bot/src/bot_api/abc/{auth_user_transformer_abc.py => transformer_abc.py} (90%) create mode 100644 kdb-bot/src/bot_api/event/bot_api_on_ready_event.py diff --git a/kdb-bot/src/bot/application.py b/kdb-bot/src/bot/application.py index 6c65a6b4..1cfa2caa 100644 --- a/kdb-bot/src/bot/application.py +++ b/kdb-bot/src/bot/application.py @@ -43,10 +43,9 @@ class Application(DiscordBotApplicationABC): async def main(self): try: self._logger.debug(__name__, f'Starting...') - if self._feature_flags.get_flag(FeatureFlagsEnum.api_module): - self._api.start() - if self._feature_flags.get_flag(FeatureFlagsEnum.api_only) and self._environment.environment_name == 'development': + if self._feature_flags.get_flag(FeatureFlagsEnum.api_module) and self._feature_flags.get_flag(FeatureFlagsEnum.api_only) and self._environment.environment_name == 'development': + self._api.start() self._api.join() return diff --git a/kdb-bot/src/bot/module_list.py b/kdb-bot/src/bot/module_list.py index 866b7d67..b818ad4b 100644 --- a/kdb-bot/src/bot/module_list.py +++ b/kdb-bot/src/bot/module_list.py @@ -21,13 +21,13 @@ class ModuleList: return List(type, [ CoreModule, # has to be first! DataModule, - ApiModule, AdminModule, AutoRoleModule, BaseModule, DatabaseModule, ModeratorModule, PermissionModule, + ApiModule, # has to be last! BootLogModule, CoreExtensionModule, diff --git a/kdb-bot/src/bot_api/abc/auth_user_transformer_abc.py b/kdb-bot/src/bot_api/abc/transformer_abc.py similarity index 90% rename from kdb-bot/src/bot_api/abc/auth_user_transformer_abc.py rename to kdb-bot/src/bot_api/abc/transformer_abc.py index fd273847..6ff2f676 100644 --- a/kdb-bot/src/bot_api/abc/auth_user_transformer_abc.py +++ b/kdb-bot/src/bot_api/abc/transformer_abc.py @@ -5,7 +5,7 @@ from cpl_core.database import TableABC from bot_api.abc.dto_abc import DtoABC -class AuthUserTransformerABC: +class TransformerABC: @staticmethod @abstractmethod diff --git a/kdb-bot/src/bot_api/api_module.py b/kdb-bot/src/bot_api/api_module.py index 6474a3e5..d6d659ec 100644 --- a/kdb-bot/src/bot_api/api_module.py +++ b/kdb-bot/src/bot_api/api_module.py @@ -4,6 +4,7 @@ from cpl_core.configuration import ConfigurationABC from cpl_core.dependency_injection import ServiceCollectionABC from cpl_core.environment import ApplicationEnvironmentABC from cpl_core.mailing import EMailClientABC, EMailClient +from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from flask import Flask @@ -12,6 +13,7 @@ from bot_api.api import Api from bot_api.api_thread import ApiThread from bot_api.controller.gui_controller import GuiController from bot_api.controller.auth_controller import AuthController +from bot_api.event.bot_api_on_ready_event import BotApiOnReadyEvent from bot_api.service.auth_service import AuthService from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum @@ -39,3 +41,6 @@ class ApiModule(ModuleABC): services.add_transient(AuthServiceABC, AuthService) services.add_transient(AuthController) services.add_transient(GuiController) + + # cpl-discord + self._dc.add_event(DiscordEventTypesEnum.on_ready.value, BotApiOnReadyEvent) diff --git a/kdb-bot/src/bot_api/event/bot_api_on_ready_event.py b/kdb-bot/src/bot_api/event/bot_api_on_ready_event.py new file mode 100644 index 00000000..ca63a03c --- /dev/null +++ b/kdb-bot/src/bot_api/event/bot_api_on_ready_event.py @@ -0,0 +1,13 @@ +from cpl_discord.events import OnReadyABC + +from bot_api.api_thread import ApiThread + + +class BotApiOnReadyEvent(OnReadyABC): + + def __init__(self, api: ApiThread): + OnReadyABC.__init__(self) + self._api = api + + async def on_ready(self): + self._api.start() diff --git a/kdb-bot/src/bot_api/transformer/auth_user_transformer.py b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py index b324445b..3f84a939 100644 --- a/kdb-bot/src/bot_api/transformer/auth_user_transformer.py +++ b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py @@ -1,12 +1,12 @@ from datetime import datetime, timezone -from bot_api.abc.auth_user_transformer_abc import AuthUserTransformerABC +from bot_api.abc.transformer_abc import TransformerABC from bot_api.model.auth_user_dto import AuthUserDTO from bot_data.model.auth_role_enum import AuthRoleEnum from bot_data.model.auth_user import AuthUser -class AuthUserTransformer(AuthUserTransformerABC): +class AuthUserTransformer(TransformerABC): @staticmethod def to_db(dto: AuthUser) -> AuthUser: From 1857473ccc1cb2ff706df625a0ca2636032dcd69 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 20:59:02 +0200 Subject: [PATCH 037/275] Added logic to get discord servers #72 --- kdb-bot/src/bot/config/feature-flags.json | 3 +- .../bot_api/controller/discord/__init__.py | 0 .../controller/discord/server_controller.py | 39 +++++++++++++++ kdb-bot/src/bot_api/event/__init__.py | 0 kdb-bot/src/bot_api/model/discord/__init__.py | 0 .../src/bot_api/model/discord/server_dto.py | 49 +++++++++++++++++++ .../src/bot_api/service/discord_service.py | 23 +++++++++ .../bot_api/transformer/server_transformer.py | 19 +++++++ 8 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 kdb-bot/src/bot_api/controller/discord/__init__.py create mode 100644 kdb-bot/src/bot_api/controller/discord/server_controller.py create mode 100644 kdb-bot/src/bot_api/event/__init__.py create mode 100644 kdb-bot/src/bot_api/model/discord/__init__.py create mode 100644 kdb-bot/src/bot_api/model/discord/server_dto.py create mode 100644 kdb-bot/src/bot_api/service/discord_service.py create mode 100644 kdb-bot/src/bot_api/transformer/server_transformer.py diff --git a/kdb-bot/src/bot/config/feature-flags.json b/kdb-bot/src/bot/config/feature-flags.json index 2d214f7f..68bf6e00 100644 --- a/kdb-bot/src/bot/config/feature-flags.json +++ b/kdb-bot/src/bot/config/feature-flags.json @@ -10,7 +10,6 @@ "DatabaseModule": true, "ModeratorModule": true, "PermissionModule": true, - "PresenceModule": true, - "ApiOnly": true + "PresenceModule": true } } diff --git a/kdb-bot/src/bot_api/controller/discord/__init__.py b/kdb-bot/src/bot_api/controller/discord/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kdb-bot/src/bot_api/controller/discord/server_controller.py b/kdb-bot/src/bot_api/controller/discord/server_controller.py new file mode 100644 index 00000000..957624a4 --- /dev/null +++ b/kdb-bot/src/bot_api/controller/discord/server_controller.py @@ -0,0 +1,39 @@ +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 + +from bot_api.api import Api +from bot_api.logging.api_logger import ApiLogger +from bot_api.route.route import Route +from bot_api.service.discord_service import DiscordService + + +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}/servers') + @Route.authorize + async def get_all_servers(self) -> Response: + return jsonify(self._discord_service.get_all_servers().select(lambda x: x.to_dict())) diff --git a/kdb-bot/src/bot_api/event/__init__.py b/kdb-bot/src/bot_api/event/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kdb-bot/src/bot_api/model/discord/__init__.py b/kdb-bot/src/bot_api/model/discord/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kdb-bot/src/bot_api/model/discord/server_dto.py b/kdb-bot/src/bot_api/model/discord/server_dto.py new file mode 100644 index 00000000..e7115dd2 --- /dev/null +++ b/kdb-bot/src/bot_api/model/discord/server_dto.py @@ -0,0 +1,49 @@ +from bot_api.abc.dto_abc import DtoABC + + +class ServerDTO(DtoABC): + + def __init__( + self, + server_id: int, + discord_id: int, + name: str, + member_count: int + + ): + DtoABC.__init__(self) + + self._server_id = server_id + self._discord_id = discord_id + self._name = name + self._member_count = member_count + + @property + def server_id(self) -> int: + return self._server_id + + @property + def discord_id(self) -> int: + return self._discord_id + + @property + def name(self) -> str: + return self._name + + @property + def member_count(self) -> int: + return self._member_count + + def from_dict(self, values: dict): + self._server_id = int(values['serverId']) + self._discord_id = int(values['discordId']) + self._name = values['name'] + self._member_count = int(values['memberCount']) + + def to_dict(self) -> dict: + return { + 'serverId': self._server_id, + 'discordId': self._discord_id, + 'name': self._name, + 'memberCount': self._member_count, + } diff --git a/kdb-bot/src/bot_api/service/discord_service.py b/kdb-bot/src/bot_api/service/discord_service.py new file mode 100644 index 00000000..448d4d7f --- /dev/null +++ b/kdb-bot/src/bot_api/service/discord_service.py @@ -0,0 +1,23 @@ +from cpl_discord.service import DiscordBotServiceABC +from cpl_query.extension import List + +from bot_api.model.discord.server_dto import ServerDTO +from bot_api.transformer.server_transformer import ServerTransformer +from bot_data.abc.server_repository_abc import ServerRepositoryABC + + +class DiscordService: + + def __init__( + self, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + ): + self._bot = bot + self._servers = servers + + def get_all_servers(self) -> List[ServerDTO]: + servers = self._servers.get_servers().select() + return servers.select( + lambda x: ServerTransformer.to_dto(x, self._bot.get_guild(x.discord_server_id).name, self._bot.get_guild(x.discord_server_id).member_count) + ) diff --git a/kdb-bot/src/bot_api/transformer/server_transformer.py b/kdb-bot/src/bot_api/transformer/server_transformer.py new file mode 100644 index 00000000..920fd506 --- /dev/null +++ b/kdb-bot/src/bot_api/transformer/server_transformer.py @@ -0,0 +1,19 @@ +from bot_api.abc.transformer_abc import TransformerABC +from bot_api.model.discord.server_dto import ServerDTO +from bot_data.model.server import Server + + +class ServerTransformer(TransformerABC): + + @staticmethod + def to_db(dto: ServerDTO) -> Server: + return Server(dto.discord_id) + + @staticmethod + def to_dto(db: Server, name: str, member_count: int) -> ServerDTO: + return ServerDTO( + db.server_id, + db.discord_server_id, + name, + member_count, + ) From dfcc516389a0a981931e640ed92bc4a83176fc42 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Mon, 17 Oct 2022 13:53:37 +0200 Subject: [PATCH 038/275] Added role check to authorization #72 --- kdb-bot/src/bot_api/api_module.py | 4 +++ .../src/bot_api/controller/auth_controller.py | 15 ++++++----- .../controller/discord/server_controller.py | 3 ++- .../exception/service_error_code_enum.py | 1 + kdb-bot/src/bot_api/route/route.py | 26 +++++++++++++++++-- kdb-bot/src/bot_api/service/auth_service.py | 4 ++- .../transformer/auth_user_transformer.py | 2 +- 7 files changed, 43 insertions(+), 12 deletions(-) diff --git a/kdb-bot/src/bot_api/api_module.py b/kdb-bot/src/bot_api/api_module.py index d6d659ec..0a9446ad 100644 --- a/kdb-bot/src/bot_api/api_module.py +++ b/kdb-bot/src/bot_api/api_module.py @@ -11,10 +11,12 @@ from flask import Flask from bot_api.abc.auth_service_abc import AuthServiceABC from bot_api.api import Api from bot_api.api_thread import ApiThread +from bot_api.controller.discord.server_controller import ServerController from bot_api.controller.gui_controller import GuiController from bot_api.controller.auth_controller import AuthController from bot_api.event.bot_api_on_ready_event import BotApiOnReadyEvent from bot_api.service.auth_service import AuthService +from bot_api.service.discord_service import DiscordService from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum @@ -41,6 +43,8 @@ class ApiModule(ModuleABC): services.add_transient(AuthServiceABC, AuthService) services.add_transient(AuthController) services.add_transient(GuiController) + services.add_transient(DiscordService) + services.add_transient(ServerController) # cpl-discord self._dc.add_event(DiscordEventTypesEnum.on_ready.value, BotApiOnReadyEvent) diff --git a/kdb-bot/src/bot_api/controller/auth_controller.py b/kdb-bot/src/bot_api/controller/auth_controller.py index a23d490d..78a17e79 100644 --- a/kdb-bot/src/bot_api/controller/auth_controller.py +++ b/kdb-bot/src/bot_api/controller/auth_controller.py @@ -15,6 +15,7 @@ from bot_api.model.auth_user_dto import AuthUserDTO from bot_api.model.token_dto import TokenDTO from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO from bot_api.route.route import Route +from bot_data.model.auth_role_enum import AuthRoleEnum class AuthController: @@ -41,13 +42,13 @@ class AuthController: self._auth_service = auth_service @Route.get(f'{BasePath}/users') - @Route.authorize + @Route.authorize(role=AuthRoleEnum.admin) async def get_all_users(self) -> Response: result = await self._auth_service.get_all_auth_users_async() return jsonify(result.select(lambda x: x.to_dict())) @Route.post(f'{BasePath}/users/get/filtered') - @Route.authorize + @Route.authorize(role=AuthRoleEnum.admin) async def get_filtered_users(self) -> Response: dto: AuthUserSelectCriteria = JSONProcessor.process(AuthUserSelectCriteria, request.get_json(force=True, silent=True)) result = await self._auth_service.get_filtered_auth_users_async(dto) @@ -55,13 +56,13 @@ class AuthController: return jsonify(result.to_dict()) @Route.get(f'{BasePath}/users/get/') - @Route.authorize + @Route.authorize(role=AuthRoleEnum.admin) async def get_user_from_email(self, email: str) -> Response: result = await self._auth_service.get_auth_user_by_email_async(email) return jsonify(result.to_dict()) @Route.get(f'{BasePath}/users/find/') - @Route.authorize + @Route.authorize(role=AuthRoleEnum.admin) async def find_user_from_email(self, email: str) -> Response: result = await self._auth_service.find_auth_user_by_email_async(email) return jsonify(result.to_dict()) @@ -109,7 +110,7 @@ class AuthController: return '', 200 @Route.post(f'{BasePath}/update-user-as-admin') - @Route.authorize + @Route.authorize(role=AuthRoleEnum.admin) async def update_user_as_admin(self): dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True)) await self._auth_service.update_user_as_admin_async(dto) @@ -129,14 +130,14 @@ class AuthController: return '', 200 @Route.post(f'{BasePath}/delete-user') - @Route.authorize + @Route.authorize(role=AuthRoleEnum.admin) async def delete_user(self): dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True)) await self._auth_service.delete_auth_user_async(dto) return '', 200 @Route.post(f'{BasePath}/delete-user-by-mail/') - @Route.authorize + @Route.authorize(role=AuthRoleEnum.admin) async def delete_user_by_mail(self, email: str): await self._auth_service.delete_auth_user_by_email_async(email) return '', 200 diff --git a/kdb-bot/src/bot_api/controller/discord/server_controller.py b/kdb-bot/src/bot_api/controller/discord/server_controller.py index 957624a4..59b7f531 100644 --- a/kdb-bot/src/bot_api/controller/discord/server_controller.py +++ b/kdb-bot/src/bot_api/controller/discord/server_controller.py @@ -8,6 +8,7 @@ from bot_api.api import Api 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: @@ -34,6 +35,6 @@ class ServerController: self._discord_service = discord_service @Route.get(f'{BasePath}/servers') - @Route.authorize + @Route.authorize(role=AuthRoleEnum.admin) async def get_all_servers(self) -> Response: return jsonify(self._discord_service.get_all_servers().select(lambda x: x.to_dict())) diff --git a/kdb-bot/src/bot_api/exception/service_error_code_enum.py b/kdb-bot/src/bot_api/exception/service_error_code_enum.py index 7b19aeb1..5848bf33 100644 --- a/kdb-bot/src/bot_api/exception/service_error_code_enum.py +++ b/kdb-bot/src/bot_api/exception/service_error_code_enum.py @@ -21,3 +21,4 @@ class ServiceErrorCode(Enum): MailError = 10 Unauthorized = 11 + Forbidden = 12 diff --git a/kdb-bot/src/bot_api/route/route.py b/kdb-bot/src/bot_api/route/route.py index ce8826bc..26bdad2b 100644 --- a/kdb-bot/src/bot_api/route/route.py +++ b/kdb-bot/src/bot_api/route/route.py @@ -1,5 +1,6 @@ +import functools from functools import wraps -from typing import Optional +from typing import Optional, Callable from flask import request, jsonify from flask_cors import cross_origin @@ -9,6 +10,7 @@ from bot_api.exception.service_error_code_enum import ServiceErrorCode from bot_api.exception.service_exception import ServiceException from bot_api.model.error_dto import ErrorDTO from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC +from bot_data.model.auth_role_enum import AuthRoleEnum class Route: @@ -23,7 +25,10 @@ class Route: cls._auth = auth @classmethod - def authorize(cls, f): + def authorize(cls, f: Callable = None, role: AuthRoleEnum = None): + if f is None: + return functools.partial(cls.authorize, role=role) + @wraps(f) async def decorator(*args, **kwargs): token = None @@ -46,6 +51,23 @@ class Route: error = ErrorDTO(ex.error_code, ex.message) return jsonify(error.to_dict()), 401 + token = cls._auth.decode_token(token) + if token is None or 'email' not in token: + ex = ServiceException(ServiceErrorCode.Unauthorized, f'Token invalid') + error = ErrorDTO(ex.error_code, ex.message) + return jsonify(error.to_dict()), 401 + + user = cls._auth_users.get_auth_user_by_email(token['email']) + if user is None: + ex = ServiceException(ServiceErrorCode.Unauthorized, f'Token invalid') + error = ErrorDTO(ex.error_code, ex.message) + return jsonify(error.to_dict()), 401 + + if role is not None and user.auth_role.value < role.value: + ex = ServiceException(ServiceErrorCode.Unauthorized, f'Role {role} required') + error = ErrorDTO(ex.error_code, ex.message) + return jsonify(error.to_dict()), 403 + return await f(*args, **kwargs) return decorator diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index e1e4a2b9..540f5d14 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -27,6 +27,8 @@ from bot_api.transformer.auth_user_transformer import AuthUserTransformer as AUT from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC from bot_data.model.auth_user import AuthUser +_email_regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' + class AuthService(AuthServiceABC): @@ -65,7 +67,7 @@ class AuthService(AuthServiceABC): @staticmethod def _is_email_valid(email: str) -> bool: - if re.match(re.compile(r'^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$'), email) is not None: + if re.fullmatch(_email_regex, email) is not None: return True return False diff --git a/kdb-bot/src/bot_api/transformer/auth_user_transformer.py b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py index 3f84a939..3506611a 100644 --- a/kdb-bot/src/bot_api/transformer/auth_user_transformer.py +++ b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py @@ -19,7 +19,7 @@ class AuthUserTransformer(TransformerABC): None, None, datetime.now(tz=timezone.utc), - AuthRoleEnum.normal if dto.auth_role is None else dto.auth_role, + AuthRoleEnum.normal if dto.auth_role is None else AuthRoleEnum(dto.auth_role), id=0 if dto.id is None else dto.id ) From d7a0706e0c333a395f539b3e911feffe7494f755 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Mon, 17 Oct 2022 16:07:33 +0200 Subject: [PATCH 039/275] Improved get server logic #72 --- kdb-bot/src/bot_api/abc/auth_service_abc.py | 7 ++++ .../controller/discord/server_controller.py | 11 +++++- kdb-bot/src/bot_api/model/auth_user_dto.py | 12 +++++++ kdb-bot/src/bot_api/service/auth_service.py | 32 +++++++++++++++++ .../src/bot_api/service/discord_service.py | 34 +++++++++++++++++-- .../transformer/auth_user_transformer.py | 6 ++-- .../src/bot_data/abc/user_repository_abc.py | 3 ++ .../src/bot_data/migration/api_migration.py | 4 ++- kdb-bot/src/bot_data/model/auth_user.py | 13 +++++++ .../service/auth_user_repository_service.py | 1 + .../service/user_repository_service.py | 15 ++++++++ 11 files changed, 132 insertions(+), 6 deletions(-) diff --git a/kdb-bot/src/bot_api/abc/auth_service_abc.py b/kdb-bot/src/bot_api/abc/auth_service_abc.py index 3a006a0d..cd54128d 100644 --- a/kdb-bot/src/bot_api/abc/auth_service_abc.py +++ b/kdb-bot/src/bot_api/abc/auth_service_abc.py @@ -1,4 +1,5 @@ from abc import ABC, abstractmethod +from typing import Optional from cpl_query.extension import List @@ -23,6 +24,12 @@ class AuthServiceABC(ABC): @abstractmethod def decode_token(self, token: str) -> dict: pass + @abstractmethod + def get_decoded_token_from_request(self) -> dict: pass + + @abstractmethod + def find_decoded_token_from_request(self) -> Optional[dict]: pass + @abstractmethod async def get_all_auth_users_async(self) -> List[AuthUserDTO]: pass diff --git a/kdb-bot/src/bot_api/controller/discord/server_controller.py b/kdb-bot/src/bot_api/controller/discord/server_controller.py index 59b7f531..39433869 100644 --- a/kdb-bot/src/bot_api/controller/discord/server_controller.py +++ b/kdb-bot/src/bot_api/controller/discord/server_controller.py @@ -37,4 +37,13 @@ class ServerController: @Route.get(f'{BasePath}/servers') @Route.authorize(role=AuthRoleEnum.admin) async def get_all_servers(self) -> Response: - return jsonify(self._discord_service.get_all_servers().select(lambda x: x.to_dict())) + result = await self._discord_service.get_all_servers() + result = result.select(lambda x: x.to_dict()) + return jsonify(result) + + @Route.get(f'{BasePath}/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()) + return jsonify(result) diff --git a/kdb-bot/src/bot_api/model/auth_user_dto.py b/kdb-bot/src/bot_api/model/auth_user_dto.py index 4fb53394..8d42e1fa 100644 --- a/kdb-bot/src/bot_api/model/auth_user_dto.py +++ b/kdb-bot/src/bot_api/model/auth_user_dto.py @@ -15,6 +15,7 @@ class AuthUserDTO(DtoABC): password: str, confirmation_id: Optional[str], auth_role: AuthRoleEnum, + user_id: Optional[int], ): DtoABC.__init__(self) @@ -25,6 +26,7 @@ class AuthUserDTO(DtoABC): self._password = password self._is_confirmed = confirmation_id is None self._auth_role = auth_role + self._user_id = user_id @property def id(self) -> int: @@ -78,6 +80,14 @@ class AuthUserDTO(DtoABC): def auth_role(self, value: AuthRoleEnum): self._auth_role = value + @property + def user_id(self) -> Optional[int]: + return self._user_id + + @user_id.setter + def user_id(self, value: Optional[int]): + self._user_id = value + def from_dict(self, values: dict): self._id = values['id'] self._first_name = values['firstName'] @@ -86,6 +96,7 @@ class AuthUserDTO(DtoABC): self._password = values['password'] self._is_confirmed = values['isConfirmed'] self._auth_role = values['authRole'] + self._user_id = values['userId'] def to_dict(self) -> dict: return { @@ -96,4 +107,5 @@ class AuthUserDTO(DtoABC): 'password': self._password, 'isConfirmed': self._is_confirmed, 'authRole': self._auth_role.value, + 'userId': self._user_id, } diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index 540f5d14..b2056530 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -9,6 +9,7 @@ from cpl_core.database.context import DatabaseContextABC from cpl_core.mailing import EMailClientABC, EMail from cpl_query.extension import List from cpl_translation import TranslatePipe +from flask import request from bot_api.abc.auth_service_abc import AuthServiceABC from bot_api.configuration.authentication_settings import AuthenticationSettings @@ -96,6 +97,37 @@ class AuthService(AuthServiceABC): algorithms=['HS256'] ) + def get_decoded_token_from_request(self) -> dict: + token = None + if 'Authorization' in request.headers: + bearer = request.headers.get('Authorization') + token = bearer.split()[1] + + if token is None: + raise ServiceException(ServiceErrorCode.Unauthorized, f'Token not set') + + return jwt.decode( + token, + key=self._auth_settings.secret_key, + issuer=self._auth_settings.issuer, + audience=self._auth_settings.audience, + algorithms=['HS256'] + ) + + def find_decoded_token_from_request(self) -> Optional[dict]: + token = None + if 'Authorization' in request.headers: + bearer = request.headers.get('Authorization') + token = bearer.split()[1] + + return jwt.decode( + token, + key=self._auth_settings.secret_key, + issuer=self._auth_settings.issuer, + audience=self._auth_settings.audience, + algorithms=['HS256'] + ) if token is not None else None + def _create_and_save_refresh_token(self, user: AuthUser) -> str: token = str(uuid.uuid4()) user.refresh_token = token diff --git a/kdb-bot/src/bot_api/service/discord_service.py b/kdb-bot/src/bot_api/service/discord_service.py index 448d4d7f..e6b3056c 100644 --- a/kdb-bot/src/bot_api/service/discord_service.py +++ b/kdb-bot/src/bot_api/service/discord_service.py @@ -1,9 +1,18 @@ +from typing import Optional + from cpl_discord.service import DiscordBotServiceABC from cpl_query.extension import List +from flask import jsonify +from bot_api.abc.auth_service_abc import AuthServiceABC +from bot_api.exception.service_error_code_enum import ServiceErrorCode +from bot_api.exception.service_exception import ServiceException from bot_api.model.discord.server_dto import ServerDTO +from bot_api.model.error_dto import ErrorDTO from bot_api.transformer.server_transformer import ServerTransformer from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.auth_role_enum import AuthRoleEnum class DiscordService: @@ -12,12 +21,33 @@ class DiscordService: self, bot: DiscordBotServiceABC, servers: ServerRepositoryABC, + auth: AuthServiceABC, + users: UserRepositoryABC, ): self._bot = bot self._servers = servers + self._auth = auth + self._users = users + + async def get_all_servers(self) -> List[ServerDTO]: + servers = self._servers.get_servers() + return servers.select( + lambda x: ServerTransformer.to_dto(x, self._bot.get_guild(x.discord_server_id).name, self._bot.get_guild(x.discord_server_id).member_count) + ) + + async def get_all_servers_by_user(self) -> List[ServerDTO]: + token = self._auth.get_decoded_token_from_request() + if token is None or 'email' not in token or 'role' not in token: + raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') + + role = AuthRoleEnum(token['role']) + if role == AuthRoleEnum.admin: + servers = self._servers.get_servers() + else: + user = await self._auth.find_auth_user_by_email_async(token['email']) + user_from_db = self._users.find_user_by_id(0 if user.user_id is None else user.user_id) + servers = self._servers.get_servers().where(lambda x: user_from_db is not None and x.server_id == user_from_db.server.server_id) - def get_all_servers(self) -> List[ServerDTO]: - servers = self._servers.get_servers().select() return servers.select( lambda x: ServerTransformer.to_dto(x, self._bot.get_guild(x.discord_server_id).name, self._bot.get_guild(x.discord_server_id).member_count) ) diff --git a/kdb-bot/src/bot_api/transformer/auth_user_transformer.py b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py index 3506611a..b5608e23 100644 --- a/kdb-bot/src/bot_api/transformer/auth_user_transformer.py +++ b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py @@ -9,7 +9,7 @@ from bot_data.model.auth_user import AuthUser class AuthUserTransformer(TransformerABC): @staticmethod - def to_db(dto: AuthUser) -> AuthUser: + def to_db(dto: AuthUserDTO) -> AuthUser: return AuthUser( dto.first_name, dto.last_name, @@ -20,6 +20,7 @@ class AuthUserTransformer(TransformerABC): None, datetime.now(tz=timezone.utc), AuthRoleEnum.normal if dto.auth_role is None else AuthRoleEnum(dto.auth_role), + dto.user_id, id=0 if dto.id is None else dto.id ) @@ -32,5 +33,6 @@ class AuthUserTransformer(TransformerABC): db.email, db.password, db.confirmation_id, - db.auth_role + db.auth_role, + db.user_id ) diff --git a/kdb-bot/src/bot_data/abc/user_repository_abc.py b/kdb-bot/src/bot_data/abc/user_repository_abc.py index ff7f6254..12878b6f 100644 --- a/kdb-bot/src/bot_data/abc/user_repository_abc.py +++ b/kdb-bot/src/bot_data/abc/user_repository_abc.py @@ -16,6 +16,9 @@ class UserRepositoryABC(ABC): @abstractmethod def get_user_by_id(self, id: int) -> User: pass + + @abstractmethod + def find_user_by_id(self, id: int) -> Optional[User]: pass @abstractmethod def get_users_by_discord_id(self, discord_id: int) -> List[User]: pass diff --git a/kdb-bot/src/bot_data/migration/api_migration.py b/kdb-bot/src/bot_data/migration/api_migration.py index 80754c88..6860ae14 100644 --- a/kdb-bot/src/bot_data/migration/api_migration.py +++ b/kdb-bot/src/bot_data/migration/api_migration.py @@ -28,9 +28,11 @@ class ApiMigration(MigrationABC): `ForgotPasswordId` VARCHAR(255) DEFAULT NULL, `RefreshTokenExpiryTime` DATETIME(6) NOT NULL, `AuthRole` INT NOT NULL DEFAULT '0', + `UserId` BIGINT NOT NULL DEFAULT '0', `CreatedOn` DATETIME(6) NOT NULL, `LastModifiedOn` DATETIME(6) NOT NULL, - PRIMARY KEY(`Id`) + PRIMARY KEY(`Id`), + FOREIGN KEY (`UserId`) REFERENCES `Users`(`UserId`) ) """) ) diff --git a/kdb-bot/src/bot_data/model/auth_user.py b/kdb-bot/src/bot_data/model/auth_user.py index 26ba09a8..fe92ad1e 100644 --- a/kdb-bot/src/bot_data/model/auth_user.py +++ b/kdb-bot/src/bot_data/model/auth_user.py @@ -19,6 +19,7 @@ class AuthUser(TableABC): forgot_password_id: Optional[str], refresh_token_expire_time: datetime, auth_role: AuthRoleEnum, + user_id: Optional[int], created_at: datetime = None, modified_at: datetime = None, id=0 @@ -34,6 +35,7 @@ class AuthUser(TableABC): self._refresh_token_expire_time = refresh_token_expire_time self._auth_role_id = auth_role + self._user_id = user_id TableABC.__init__(self) self._created_at = created_at if created_at is not None else self._created_at @@ -115,6 +117,14 @@ class AuthUser(TableABC): def auth_role(self, value: AuthRoleEnum): self._auth_role_id = value + @property + def user_id(self) -> Optional[int]: + return self._user_id + + @user_id.setter + def user_id(self, value: Optional[int]): + self._user_id = value + @staticmethod def get_select_all_string() -> str: return str(f""" @@ -163,6 +173,7 @@ class AuthUser(TableABC): `ForgotPasswordId`, `RefreshTokenExpiryTime`, `AuthRole`, + `UserId`, `CreatedOn`, `LastModifiedOn` ) VALUES ( @@ -176,6 +187,7 @@ class AuthUser(TableABC): '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', '{self._refresh_token_expire_time}', {self._auth_role_id.value}, + {"NULL" if self._user_id is None else self._user_id} '{self._created_at}', '{self._modified_at}' ) @@ -194,6 +206,7 @@ class AuthUser(TableABC): `ForgotPasswordId` = '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', `RefreshTokenExpiryTime` = '{self._refresh_token_expire_time}', `AuthRole` = {self._auth_role_id.value}, + `UserId` = {"NULL" if self._user_id is None else self._user_id}, `LastModifiedOn` = '{self._modified_at}' WHERE `AuthUsers`.`Id` = {self._auth_user_id}; """) diff --git a/kdb-bot/src/bot_data/service/auth_user_repository_service.py b/kdb-bot/src/bot_data/service/auth_user_repository_service.py index 4918b269..c8fee7b4 100644 --- a/kdb-bot/src/bot_data/service/auth_user_repository_service.py +++ b/kdb-bot/src/bot_data/service/auth_user_repository_service.py @@ -37,6 +37,7 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): self._get_value_from_result(result[7]), self._get_value_from_result(result[8]), AuthRoleEnum(self._get_value_from_result(result[9])), + self._get_value_from_result(result[10]), id=self._get_value_from_result(result[0]) ) diff --git a/kdb-bot/src/bot_data/service/user_repository_service.py b/kdb-bot/src/bot_data/service/user_repository_service.py index f2543e5d..83f98627 100644 --- a/kdb-bot/src/bot_data/service/user_repository_service.py +++ b/kdb-bot/src/bot_data/service/user_repository_service.py @@ -44,6 +44,21 @@ class UserRepositoryService(UserRepositoryABC): self._servers.get_server_by_id(result[3]), id=result[0] ) + + def find_user_by_id(self, id: int) -> Optional[User]: + self._logger.trace(__name__, f'Send SQL command: {User.get_select_by_id_string(id)}') + result = self._context.select(User.get_select_by_id_string(id)) + if result is None or len(result) == 0: + return None + + result = result[0] + + return User( + result[1], + result[2], + self._servers.get_server_by_id(result[3]), + id=result[0] + ) def get_users_by_discord_id(self, discord_id: int) -> List[User]: users = List(User) From 3d17bb7703042236f58272be2beba8c0d4a9829e Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Mon, 17 Oct 2022 16:22:09 +0200 Subject: [PATCH 040/275] Added get filtered servers #72 --- .../controller/discord/server_controller.py | 12 ++++++++- .../src/bot_api/filter/discord/__init__.py | 0 .../filter/discord/server_select_criteria.py | 17 +++++++++++++ .../discord/server_filtered_result_dto.py | 21 ++++++++++++++++ .../src/bot_api/service/discord_service.py | 25 +++++++++++++++++++ .../src/bot_data/abc/server_repository_abc.py | 5 ++++ .../service/server_repository_service.py | 25 +++++++++++++++++++ 7 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 kdb-bot/src/bot_api/filter/discord/__init__.py create mode 100644 kdb-bot/src/bot_api/filter/discord/server_select_criteria.py create mode 100644 kdb-bot/src/bot_api/model/discord/server_filtered_result_dto.py diff --git a/kdb-bot/src/bot_api/controller/discord/server_controller.py b/kdb-bot/src/bot_api/controller/discord/server_controller.py index 39433869..35a3fa9c 100644 --- a/kdb-bot/src/bot_api/controller/discord/server_controller.py +++ b/kdb-bot/src/bot_api/controller/discord/server_controller.py @@ -2,9 +2,11 @@ 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 +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 @@ -41,6 +43,14 @@ class ServerController: result = result.select(lambda x: x.to_dict()) return jsonify(result) + @Route.get(f'{BasePath}/servers/get/filtered') + @Route.authorize + async def get_all_servers_by_user(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()) + return jsonify(result.to_dict()) + @Route.get(f'{BasePath}/servers-by-user') @Route.authorize async def get_all_servers_by_user(self) -> Response: diff --git a/kdb-bot/src/bot_api/filter/discord/__init__.py b/kdb-bot/src/bot_api/filter/discord/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kdb-bot/src/bot_api/filter/discord/server_select_criteria.py b/kdb-bot/src/bot_api/filter/discord/server_select_criteria.py new file mode 100644 index 00000000..f1454a8c --- /dev/null +++ b/kdb-bot/src/bot_api/filter/discord/server_select_criteria.py @@ -0,0 +1,17 @@ +from bot_api.abc.select_criteria_abc import SelectCriteriaABC + + +class ServerSelectCriteria(SelectCriteriaABC): + + def __init__( + self, + page_index: int, + page_size: int, + sort_direction: str, + sort_column: str, + + name: str, + ): + SelectCriteriaABC.__init__(self, page_index, page_size, sort_direction, sort_column) + + self.name = name diff --git a/kdb-bot/src/bot_api/model/discord/server_filtered_result_dto.py b/kdb-bot/src/bot_api/model/discord/server_filtered_result_dto.py new file mode 100644 index 00000000..eceb642c --- /dev/null +++ b/kdb-bot/src/bot_api/model/discord/server_filtered_result_dto.py @@ -0,0 +1,21 @@ +from cpl_query.extension import List + +from bot_api.abc.dto_abc import DtoABC +from bot_data.filtered_result import FilteredResult + + +class ServerFilteredResultDTO(DtoABC, FilteredResult): + + def __init__(self, result: List = None, total_count: int = 0): + DtoABC.__init__(self) + FilteredResult.__init__(self, result, total_count) + + def from_dict(self, values: dict): + self._result = values['servers'] + self._total_count = values['totalCount'] + + def to_dict(self) -> dict: + return { + 'servers': self.result, + 'totalCount': self.total_count + } diff --git a/kdb-bot/src/bot_api/service/discord_service.py b/kdb-bot/src/bot_api/service/discord_service.py index e6b3056c..e00e5b84 100644 --- a/kdb-bot/src/bot_api/service/discord_service.py +++ b/kdb-bot/src/bot_api/service/discord_service.py @@ -7,7 +7,9 @@ from flask import jsonify from bot_api.abc.auth_service_abc import AuthServiceABC from bot_api.exception.service_error_code_enum import ServiceErrorCode from bot_api.exception.service_exception import ServiceException +from bot_api.filter.discord.server_select_criteria import ServerSelectCriteria from bot_api.model.discord.server_dto import ServerDTO +from bot_api.model.discord.server_filtered_result_dto import ServerFilteredResultDTO from bot_api.model.error_dto import ErrorDTO from bot_api.transformer.server_transformer import ServerTransformer from bot_data.abc.server_repository_abc import ServerRepositoryABC @@ -35,6 +37,29 @@ class DiscordService: lambda x: ServerTransformer.to_dto(x, self._bot.get_guild(x.discord_server_id).name, self._bot.get_guild(x.discord_server_id).member_count) ) + async def get_filtered_servers_async(self, criteria: ServerSelectCriteria) -> ServerFilteredResultDTO: + token = self._auth.get_decoded_token_from_request() + if token is None or 'email' not in token or 'role' not in token: + raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') + + role = AuthRoleEnum(token['role']) + role = AuthRoleEnum(token['role']) + filtered_result = self._servers.get_filtered_servers(criteria) + servers = filtered_result.result + if role != AuthRoleEnum.admin: + user = await self._auth.find_auth_user_by_email_async(token['email']) + user_from_db = self._users.find_user_by_id(0 if user.user_id is None else user.user_id) + servers = servers.where(lambda x: user_from_db is not None and x.server_id == user_from_db.server.server_id) + + result = servers.select( + lambda x: ServerTransformer.to_dto(x, self._bot.get_guild(x.discord_server_id).name, self._bot.get_guild(x.discord_server_id).member_count) + ) + + return ServerFilteredResultDTO( + List(ServerDTO, result), + servers.total_count + ) + async def get_all_servers_by_user(self) -> List[ServerDTO]: token = self._auth.get_decoded_token_from_request() if token is None or 'email' not in token or 'role' not in token: diff --git a/kdb-bot/src/bot_data/abc/server_repository_abc.py b/kdb-bot/src/bot_data/abc/server_repository_abc.py index d1ab13fe..f3eea75a 100644 --- a/kdb-bot/src/bot_data/abc/server_repository_abc.py +++ b/kdb-bot/src/bot_data/abc/server_repository_abc.py @@ -3,6 +3,8 @@ from typing import Optional from cpl_query.extension import List +from bot_api.filter.discord.server_select_criteria import ServerSelectCriteria +from bot_data.filtered_result import FilteredResult from bot_data.model.server import Server @@ -13,6 +15,9 @@ class ServerRepositoryABC(ABC): @abstractmethod def get_servers(self) -> List[Server]: pass + + @abstractmethod + def get_filtered_servers(self, criteria: ServerSelectCriteria) -> FilteredResult: pass @abstractmethod def get_server_by_id(self, id: int) -> Server: pass diff --git a/kdb-bot/src/bot_data/service/server_repository_service.py b/kdb-bot/src/bot_data/service/server_repository_service.py index 728d2bc6..37b2918e 100644 --- a/kdb-bot/src/bot_data/service/server_repository_service.py +++ b/kdb-bot/src/bot_data/service/server_repository_service.py @@ -3,8 +3,10 @@ from typing import Optional from cpl_core.database.context import DatabaseContextABC from cpl_query.extension import List +from bot_api.filter.discord.server_select_criteria import ServerSelectCriteria from bot_core.logging.database_logger import DatabaseLogger from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.filtered_result import FilteredResult from bot_data.model.server import Server @@ -28,6 +30,29 @@ class ServerRepositoryService(ServerRepositoryABC): return servers + def get_filtered_servers(self, criteria: ServerSelectCriteria) -> FilteredResult: + servers = self.get_servers() + self._logger.trace(__name__, f'Send SQL command: {Server.get_select_all_string()}') + query = servers + + if criteria.name is not None and criteria.name != '': + query = query.where(lambda x: criteria.name in x.first_name or x.first_name == criteria.name) + + # sort + if criteria.sort_column is not None and criteria.sort_column != '' and criteria.sort_direction is not None and criteria.sort_direction: + crit_sort_direction = criteria.sort_direction.lower() + if crit_sort_direction == "desc" or crit_sort_direction == "descending": + query = query.order_by_descending(lambda x: getattr(x, criteria.sort_column)) + else: + query = query.order_by(lambda x: getattr(x, criteria.sort_column)) + + result = FilteredResult() + result.total_count = query.count() + skip = criteria.page_size * criteria.page_index + result.result = query.skip(skip).take(criteria.page_size) + + return result + def get_server_by_id(self, server_id: int) -> Server: self._logger.trace(__name__, f'Send SQL command: {Server.get_select_by_id_string(server_id)}') result = self._context.select(Server.get_select_by_id_string(server_id))[0] From 1baa8cee60036e4e15cc2e103db62fc273c9259e Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Mon, 17 Oct 2022 16:50:09 +0200 Subject: [PATCH 041/275] Added logic to get servers to dashboard #72 --- .../controller/discord/server_controller.py | 20 ++-- .../src/bot_api/service/discord_service.py | 45 +++++---- kdb-web/src/app/models/discord/server.dto.ts | 6 ++ .../logins/login-select-criterion.dto.ts | 8 -- .../server/get-filtered-servers-result.dto.ts | 7 ++ .../server/server-select-criterion.dto.ts | 5 + .../auth-user/auth-user.component.ts | 2 +- .../dashboard/dashboard.component.html | 15 ++- .../dashboard/dashboard.component.ts | 95 ++++++++++++++++++- kdb-web/src/app/services/data/data.service.ts | 33 ++++++- kdb-web/src/assets/i18n/de.json | 4 +- 11 files changed, 191 insertions(+), 49 deletions(-) create mode 100644 kdb-web/src/app/models/discord/server.dto.ts delete mode 100644 kdb-web/src/app/models/selection/logins/login-select-criterion.dto.ts create mode 100644 kdb-web/src/app/models/selection/server/get-filtered-servers-result.dto.ts create mode 100644 kdb-web/src/app/models/selection/server/server-select-criterion.dto.ts diff --git a/kdb-bot/src/bot_api/controller/discord/server_controller.py b/kdb-bot/src/bot_api/controller/discord/server_controller.py index 35a3fa9c..d2c89b10 100644 --- a/kdb-bot/src/bot_api/controller/discord/server_controller.py +++ b/kdb-bot/src/bot_api/controller/discord/server_controller.py @@ -36,24 +36,24 @@ class ServerController: self._mailer = mailer self._discord_service = discord_service - @Route.get(f'{BasePath}/servers') + @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()) return jsonify(result) - @Route.get(f'{BasePath}/servers/get/filtered') - @Route.authorize - async def get_all_servers_by_user(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()) - return jsonify(result.to_dict()) - - @Route.get(f'{BasePath}/servers-by-user') + @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()) 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()) + return jsonify(result.to_dict()) diff --git a/kdb-bot/src/bot_api/service/discord_service.py b/kdb-bot/src/bot_api/service/discord_service.py index e00e5b84..6f8e5f13 100644 --- a/kdb-bot/src/bot_api/service/discord_service.py +++ b/kdb-bot/src/bot_api/service/discord_service.py @@ -37,29 +37,6 @@ class DiscordService: lambda x: ServerTransformer.to_dto(x, self._bot.get_guild(x.discord_server_id).name, self._bot.get_guild(x.discord_server_id).member_count) ) - async def get_filtered_servers_async(self, criteria: ServerSelectCriteria) -> ServerFilteredResultDTO: - token = self._auth.get_decoded_token_from_request() - if token is None or 'email' not in token or 'role' not in token: - raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') - - role = AuthRoleEnum(token['role']) - role = AuthRoleEnum(token['role']) - filtered_result = self._servers.get_filtered_servers(criteria) - servers = filtered_result.result - if role != AuthRoleEnum.admin: - user = await self._auth.find_auth_user_by_email_async(token['email']) - user_from_db = self._users.find_user_by_id(0 if user.user_id is None else user.user_id) - servers = servers.where(lambda x: user_from_db is not None and x.server_id == user_from_db.server.server_id) - - result = servers.select( - lambda x: ServerTransformer.to_dto(x, self._bot.get_guild(x.discord_server_id).name, self._bot.get_guild(x.discord_server_id).member_count) - ) - - return ServerFilteredResultDTO( - List(ServerDTO, result), - servers.total_count - ) - async def get_all_servers_by_user(self) -> List[ServerDTO]: token = self._auth.get_decoded_token_from_request() if token is None or 'email' not in token or 'role' not in token: @@ -76,3 +53,25 @@ class DiscordService: return servers.select( lambda x: ServerTransformer.to_dto(x, self._bot.get_guild(x.discord_server_id).name, self._bot.get_guild(x.discord_server_id).member_count) ) + + async def get_filtered_servers_async(self, criteria: ServerSelectCriteria) -> ServerFilteredResultDTO: + token = self._auth.get_decoded_token_from_request() + if token is None or 'email' not in token or 'role' not in token: + raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') + + role = AuthRoleEnum(token['role']) + filtered_result = self._servers.get_filtered_servers(criteria) + servers = filtered_result.result + if role != AuthRoleEnum.admin: + user = await self._auth.find_auth_user_by_email_async(token['email']) + user_from_db = self._users.find_user_by_id(0 if user.user_id is None else user.user_id) + servers = servers.where(lambda x: user_from_db is not None and x.server_id == user_from_db.server.server_id) + + result = servers.select( + lambda x: ServerTransformer.to_dto(x, self._bot.get_guild(x.discord_server_id).name, self._bot.get_guild(x.discord_server_id).member_count) + ) + + return ServerFilteredResultDTO( + List(ServerDTO, result), + filtered_result.total_count + ) diff --git a/kdb-web/src/app/models/discord/server.dto.ts b/kdb-web/src/app/models/discord/server.dto.ts new file mode 100644 index 00000000..4cba26ac --- /dev/null +++ b/kdb-web/src/app/models/discord/server.dto.ts @@ -0,0 +1,6 @@ +export interface ServerDTO { + serverId: number; + discordId: number; + name: string; + memberCount: number; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/selection/logins/login-select-criterion.dto.ts b/kdb-web/src/app/models/selection/logins/login-select-criterion.dto.ts deleted file mode 100644 index e1998d60..00000000 --- a/kdb-web/src/app/models/selection/logins/login-select-criterion.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { SelectCriterion } from "../select-criterion.model"; - -export interface LoginSelectCriterion extends SelectCriterion { - timeFrom: string; - timeTo: string; - userName: string; - hostName: string; -} \ No newline at end of file diff --git a/kdb-web/src/app/models/selection/server/get-filtered-servers-result.dto.ts b/kdb-web/src/app/models/selection/server/get-filtered-servers-result.dto.ts new file mode 100644 index 00000000..4dfe801a --- /dev/null +++ b/kdb-web/src/app/models/selection/server/get-filtered-servers-result.dto.ts @@ -0,0 +1,7 @@ +import { AuthUserDTO } from "../../auth/auth-user.dto"; +import { ServerDTO } from "../../discord/server.dto"; + +export interface GetFilteredServersResultDTO { + servers: ServerDTO[]; + totalCount: number; +} \ No newline at end of file diff --git a/kdb-web/src/app/models/selection/server/server-select-criterion.dto.ts b/kdb-web/src/app/models/selection/server/server-select-criterion.dto.ts new file mode 100644 index 00000000..08b9ea59 --- /dev/null +++ b/kdb-web/src/app/models/selection/server/server-select-criterion.dto.ts @@ -0,0 +1,5 @@ +import { SelectCriterion } from "../select-criterion.model"; + +export interface ServerSelectCriterion extends SelectCriterion { + name: string | null; +} \ No newline at end of file diff --git a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts index c972e7f2..a9243563 100644 --- a/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts +++ b/kdb-web/src/app/modules/admin/auth-users/components/auth-user/auth-user.component.ts @@ -85,7 +85,7 @@ export class AuthUserComponent implements OnInit { }; this.setFilterForm(); - // this.loadNextPage(); + this.loadNextPage(); } setFilterForm() { diff --git a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html index 04e795ec..e4139bac 100644 --- a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html +++ b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html @@ -1,3 +1,12 @@ -

dashboard works!

- -
+

+ {{'view.dashboard.header' | translate}} +

+
+
+
    +
  • + {{server.name}} +
  • +
+
+
\ No newline at end of file diff --git a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts index 25d48a7a..0fb3ff19 100644 --- a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts +++ b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts @@ -1,4 +1,14 @@ import { Component, OnInit } from '@angular/core'; +import { FormGroup, FormControl, FormBuilder } from '@angular/forms'; +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 { SpinnerService } from 'src/app/services/spinner/spinner.service'; +import { ToastService } from 'src/app/services/toast/toast.service'; @Component({ selector: 'app-dashboard', @@ -7,8 +17,89 @@ import { Component, OnInit } from '@angular/core'; }) export class DashboardComponent implements OnInit { - constructor() { } + loading = true; + servers: ServerDTO[] = []; - ngOnInit(): void {} + searchCriterions: ServerSelectCriterion = { + name: null, + pageIndex: 0, + pageSize: 10, + sortColumn: null, + sortDirection: null + }; + totalRecords!: number; + + + filterForm!: FormGroup<{ + name: FormControl, + }>; + + constructor( + private data: DataService, + private spinnerService: SpinnerService, + private toastService: ToastService, + private confirmDialog: ConfirmationDialogService, + private fb: FormBuilder, + private translate: TranslateService + ) { } + + ngOnInit(): void { + this.setFilterForm(); + this.loadNextPage(); + } + + setFilterForm() { + this.filterForm = this.fb.group({ + name: [''], + }); + + this.filterForm.valueChanges.pipe( + debounceTime(600) + ).subscribe(changes => { + if (changes.name) { + this.searchCriterions.name = changes.name; + } else { + this.searchCriterions.name = null; + } + + if (this.searchCriterions.pageSize) + this.searchCriterions.pageSize = 10; + + if (this.searchCriterions.pageSize) + this.searchCriterions.pageIndex = 0; + + this.loadNextPage(); + }); + } + + loadNextPage() { + this.data.getFilteredServers(this.searchCriterions).pipe(catchError(err => { + this.loading = false; + return throwError(() => err); + })).subscribe(list => { + this.totalRecords = list.totalCount; + this.servers = list.servers; + this.loading = false; + }); + } + + nextPage(event: LazyLoadEvent) { + this.searchCriterions.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'; + + if (event.filters) { + // + "" => convert to string + this.searchCriterions.name = event.filters['name'] ? event.filters['name'] + "" : null; + } + + this.loadNextPage(); + } + + resetFilters() { + this.filterForm.reset(); + } } diff --git a/kdb-web/src/app/services/data/data.service.ts b/kdb-web/src/app/services/data/data.service.ts index 36cccbb5..80a60600 100644 --- a/kdb-web/src/app/services/data/data.service.ts +++ b/kdb-web/src/app/services/data/data.service.ts @@ -1,5 +1,9 @@ -import { HttpClient } from '@angular/common/http'; +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'; @Injectable({ @@ -11,4 +15,31 @@ export class DataService { private appsettings: SettingsService, private http: HttpClient, ) { } + + + + /* data requests */ + getAllServers(): Observable> { + return this.http.get>(`${this.appsettings.getApiURL()}/api/discord/server/servers`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + getAllServersByUser(): Observable> { + return this.http.get>(`${this.appsettings.getApiURL()}/api/discord/server/servers-by-user`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + getFilteredServers(selectCriterions: ServerSelectCriterion): Observable { + return this.http.post(`${this.appsettings.getApiURL()}/api/discord/server/get/filtered`, selectCriterions, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } } diff --git a/kdb-web/src/assets/i18n/de.json b/kdb-web/src/assets/i18n/de.json index 894bd461..ae0a0f4c 100644 --- a/kdb-web/src/assets/i18n/de.json +++ b/kdb-web/src/assets/i18n/de.json @@ -106,7 +106,9 @@ } }, "view": { - "dashboard": {}, + "dashboard": { + "header": "Dashboard" + }, "user-list": {}, "change-password": { "header": "Passwort ändern", From a69c223a332b503134f542abd76e51b7b94c4c3c Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Mon, 17 Oct 2022 18:12:43 +0200 Subject: [PATCH 042/275] Added server list to dashboard #72 --- .../src/bot_api/model/discord/server_dto.py | 13 +++- .../src/bot_api/service/discord_service.py | 28 ++++++-- .../bot_api/transformer/server_transformer.py | 7 +- .../service/server_repository_service.py | 3 - kdb-web/src/app/models/discord/server.dto.ts | 1 + .../dashboard/dashboard.component.html | 48 +++++++++++-- kdb-web/src/assets/i18n/de.json | 11 ++- kdb-web/src/styles.scss | 67 +++++++++++++++++-- .../src/styles/themes/default-dark-theme.scss | 2 +- .../styles/themes/sh-edraft-dark-theme.scss | 43 +++++++++--- 10 files changed, 190 insertions(+), 33 deletions(-) diff --git a/kdb-bot/src/bot_api/model/discord/server_dto.py b/kdb-bot/src/bot_api/model/discord/server_dto.py index e7115dd2..07cd4365 100644 --- a/kdb-bot/src/bot_api/model/discord/server_dto.py +++ b/kdb-bot/src/bot_api/model/discord/server_dto.py @@ -1,3 +1,5 @@ +from typing import Optional + from bot_api.abc.dto_abc import DtoABC @@ -8,7 +10,8 @@ class ServerDTO(DtoABC): server_id: int, discord_id: int, name: str, - member_count: int + member_count: int, + icon_url: Optional[str] ): DtoABC.__init__(self) @@ -17,6 +20,7 @@ class ServerDTO(DtoABC): self._discord_id = discord_id self._name = name self._member_count = member_count + self._icon_url = icon_url @property def server_id(self) -> int: @@ -34,11 +38,15 @@ class ServerDTO(DtoABC): def member_count(self) -> int: return self._member_count + @property + def icon_url(self) -> Optional[str]: + return self._icon_url + def from_dict(self, values: dict): self._server_id = int(values['serverId']) self._discord_id = int(values['discordId']) self._name = values['name'] - self._member_count = int(values['memberCount']) + self._icon_url = int(values['iconURL']) def to_dict(self) -> dict: return { @@ -46,4 +54,5 @@ class ServerDTO(DtoABC): 'discordId': self._discord_id, 'name': self._name, 'memberCount': self._member_count, + 'iconURL': self._icon_url, } diff --git a/kdb-bot/src/bot_api/service/discord_service.py b/kdb-bot/src/bot_api/service/discord_service.py index 6f8e5f13..a0163dcc 100644 --- a/kdb-bot/src/bot_api/service/discord_service.py +++ b/kdb-bot/src/bot_api/service/discord_service.py @@ -33,9 +33,12 @@ class DiscordService: async def get_all_servers(self) -> List[ServerDTO]: servers = self._servers.get_servers() - return servers.select( - lambda x: ServerTransformer.to_dto(x, self._bot.get_guild(x.discord_server_id).name, self._bot.get_guild(x.discord_server_id).member_count) - ) + return servers.select(lambda x: ServerTransformer.to_dto( + x, + self._bot.get_guild(x.discord_server_id).name, + self._bot.get_guild(x.discord_server_id).member_count, + self._bot.get_guild(x.discord_server_id).icon + )) async def get_all_servers_by_user(self) -> List[ServerDTO]: token = self._auth.get_decoded_token_from_request() @@ -50,9 +53,12 @@ class DiscordService: user_from_db = self._users.find_user_by_id(0 if user.user_id is None else user.user_id) servers = self._servers.get_servers().where(lambda x: user_from_db is not None and x.server_id == user_from_db.server.server_id) - return servers.select( - lambda x: ServerTransformer.to_dto(x, self._bot.get_guild(x.discord_server_id).name, self._bot.get_guild(x.discord_server_id).member_count) - ) + return servers.select(lambda x: ServerTransformer.to_dto( + x, + self._bot.get_guild(x.discord_server_id).name, + self._bot.get_guild(x.discord_server_id).member_count, + self._bot.get_guild(x.discord_server_id).icon + )) async def get_filtered_servers_async(self, criteria: ServerSelectCriteria) -> ServerFilteredResultDTO: token = self._auth.get_decoded_token_from_request() @@ -68,9 +74,17 @@ class DiscordService: servers = servers.where(lambda x: user_from_db is not None and x.server_id == user_from_db.server.server_id) result = servers.select( - lambda x: ServerTransformer.to_dto(x, self._bot.get_guild(x.discord_server_id).name, self._bot.get_guild(x.discord_server_id).member_count) + lambda x: ServerTransformer.to_dto( + x, + self._bot.get_guild(x.discord_server_id).name, + self._bot.get_guild(x.discord_server_id).member_count, + self._bot.get_guild(x.discord_server_id).icon + ) ) + if criteria.name is not None and criteria.name != '': + result = result.where(lambda x: criteria.name.lower() in x.name.lower() or x.name.lower() == criteria.name.lower()) + return ServerFilteredResultDTO( List(ServerDTO, result), filtered_result.total_count diff --git a/kdb-bot/src/bot_api/transformer/server_transformer.py b/kdb-bot/src/bot_api/transformer/server_transformer.py index 920fd506..d883c42d 100644 --- a/kdb-bot/src/bot_api/transformer/server_transformer.py +++ b/kdb-bot/src/bot_api/transformer/server_transformer.py @@ -1,3 +1,7 @@ +from typing import Optional + +import discord + from bot_api.abc.transformer_abc import TransformerABC from bot_api.model.discord.server_dto import ServerDTO from bot_data.model.server import Server @@ -10,10 +14,11 @@ class ServerTransformer(TransformerABC): return Server(dto.discord_id) @staticmethod - def to_dto(db: Server, name: str, member_count: int) -> ServerDTO: + def to_dto(db: Server, name: str, member_count: int, icon_url: Optional[discord.Asset]) -> ServerDTO: return ServerDTO( db.server_id, db.discord_server_id, name, member_count, + icon_url.url if icon_url is not None else None, ) diff --git a/kdb-bot/src/bot_data/service/server_repository_service.py b/kdb-bot/src/bot_data/service/server_repository_service.py index 37b2918e..210a6dee 100644 --- a/kdb-bot/src/bot_data/service/server_repository_service.py +++ b/kdb-bot/src/bot_data/service/server_repository_service.py @@ -35,9 +35,6 @@ class ServerRepositoryService(ServerRepositoryABC): self._logger.trace(__name__, f'Send SQL command: {Server.get_select_all_string()}') query = servers - if criteria.name is not None and criteria.name != '': - query = query.where(lambda x: criteria.name in x.first_name or x.first_name == criteria.name) - # sort if criteria.sort_column is not None and criteria.sort_column != '' and criteria.sort_direction is not None and criteria.sort_direction: crit_sort_direction = criteria.sort_direction.lower() diff --git a/kdb-web/src/app/models/discord/server.dto.ts b/kdb-web/src/app/models/discord/server.dto.ts index 4cba26ac..a97625b5 100644 --- a/kdb-web/src/app/models/discord/server.dto.ts +++ b/kdb-web/src/app/models/discord/server.dto.ts @@ -3,4 +3,5 @@ export interface ServerDTO { discordId: number; name: string; memberCount: number; + iconURL: string | null; } \ No newline at end of file diff --git a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html index e4139bac..c7dd5fdf 100644 --- a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html +++ b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html @@ -2,11 +2,49 @@ {{'view.dashboard.header' | translate}}
+
+

+ + {{'view.dashboard.server.header' | translate}} +

+
+
-
    -
  • - {{server.name}} -
  • -
+
+
+
+
+ +
+
+
+ +
+ {{servers.length}} {{'view.dashboard.of' | translate}} {{totalRecords}} {{'view.dashboard.servers' | translate}}: +
+
+ +
+
+ + +
+

+ {{server.name}} +

+ +
+ + {{server.memberCount}} + {{'view.dashboard.server.member_count' | translate}} +
+
+ +
+
+
\ No newline at end of file diff --git a/kdb-web/src/assets/i18n/de.json b/kdb-web/src/assets/i18n/de.json index ae0a0f4c..83fd1ae3 100644 --- a/kdb-web/src/assets/i18n/de.json +++ b/kdb-web/src/assets/i18n/de.json @@ -107,7 +107,16 @@ }, "view": { "dashboard": { - "header": "Dashboard" + "header": "Dashboard", + "of": "von", + "servers": "Server", + "server": { + "header": "Server", + "member_count": "Mitglid(er)" + }, + "filter": { + "name": "Name" + } }, "user-list": {}, "change-password": { diff --git a/kdb-web/src/styles.scss b/kdb-web/src/styles.scss index ee146861..8bbd6cd8 100644 --- a/kdb-web/src/styles.scss +++ b/kdb-web/src/styles.scss @@ -10,6 +10,8 @@ body { height: 100%; padding: 0; margin: 0; + + font-size: 1rem; } main { @@ -18,15 +20,19 @@ main { min-height: 100vh; } -h1, -h2 { +h1 { margin: 0; - font-size: 30px; + font-size: 1.75rem; } h2 { margin: 0; - font-size: 25px; + font-size: 1.5rem; +} + +h3 { + margin: 0; + font-size: 1.25rem; } header { @@ -207,6 +213,59 @@ header { .table-header-small-dropdown { width: 150px; } + + .server-list-wrapper { + display: flex; + flex-direction: column; + gap: 10px; + + .server-filter { + } + + .server-count { + } + + .server-list { + display: flex; + flex-direction: column; + + gap: 15px; + + .server { + display: flex; + gap: 15px; + + padding: 20px; + + .logo { + overflow: hidden; + + img { + width: 4rem; + height: 4rem; + object-fit: contain; + } + } + + .info { + display: flex; + flex-direction: column; + + gap: 10px; + + .name { + margin: 0px; + + justify-content: center; + align-items: center; + } + + .data { + } + } + } + } + } } } } diff --git a/kdb-web/src/styles/themes/default-dark-theme.scss b/kdb-web/src/styles/themes/default-dark-theme.scss index 7507a6cb..1a3bd5d1 100644 --- a/kdb-web/src/styles/themes/default-dark-theme.scss +++ b/kdb-web/src/styles/themes/default-dark-theme.scss @@ -432,7 +432,7 @@ color: $primaryTextColor !important; border: 0 !important; padding: 0px !important; - + &:hover { background-color: transparent !important; color: $primaryHeaderColor !important; diff --git a/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss b/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss index ee2da9f4..8fa192f1 100644 --- a/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss +++ b/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss @@ -15,17 +15,10 @@ $primaryErrorColor: #b00020; $secondaryErrorColor: #e94948; - $default-border: 1px solid $secondaryBackgroundColor3; + $default-border: 2px solid $secondaryBackgroundColor3; background-color: $primaryBackgroundColor !important; - html, - body { - margin: 0; - - font-size: 16px; - } - h1, h2 { color: $primaryHeaderColor; @@ -122,6 +115,35 @@ .content-divider { border-bottom: $default-border; } + + .server-list-wrapper { + .server-filter { + } + + .server-count { + } + + .server-list { + .server { + border: $default-border; + border-radius: 15px; + + .logo { + img { + border-radius: 100%; + } + } + + .name { + color: $primaryHeaderColor; + } + + &:hover { + border-color: $primaryHeaderColor !important; + } + } + } + } } } } @@ -390,6 +412,9 @@ input, .p-password { + border-radius: 10px; + border: $default-border; + &:focus { box-shadow: none !important; } @@ -434,7 +459,7 @@ color: $primaryTextColor !important; border: 0 !important; padding: 0px !important; - + &:hover { background-color: transparent !important; color: $primaryHeaderColor !important; From 2a974384172e127f835afc1daa2ac013c0297c02 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Mon, 17 Oct 2022 19:44:28 +0200 Subject: [PATCH 043/275] [WIP] Added server dashboard #72 --- .../controller/discord/server_controller.py | 6 +++ .../src/bot_api/service/discord_service.py | 7 +++ kdb-web/src/app/app-routing.module.ts | 1 + .../dashboard/dashboard.component.html | 2 +- .../dashboard/dashboard.component.ts | 17 ++++--- .../dashboard/dashboard-routing.module.ts | 2 +- .../view/dashboard/dashboard.module.ts | 6 +-- .../server-dashboard.component.html | 36 +++++++++++++++ .../server-dashboard.component.scss | 0 .../server-dashboard.component.spec.ts | 23 ++++++++++ .../server-dashboard.component.ts | 45 +++++++++++++++++++ .../view/server/server-routing.module.ts | 14 ++++++ .../app/modules/view/server/server.module.ts | 19 ++++++++ kdb-web/src/app/services/data/data.service.ts | 8 ++++ kdb-web/src/assets/i18n/de.json | 9 ++-- 15 files changed, 181 insertions(+), 14 deletions(-) create mode 100644 kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.html create mode 100644 kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.scss create mode 100644 kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.spec.ts create mode 100644 kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.ts create mode 100644 kdb-web/src/app/modules/view/server/server-routing.module.ts create mode 100644 kdb-web/src/app/modules/view/server/server.module.ts diff --git a/kdb-bot/src/bot_api/controller/discord/server_controller.py b/kdb-bot/src/bot_api/controller/discord/server_controller.py index d2c89b10..1c362ab9 100644 --- a/kdb-bot/src/bot_api/controller/discord/server_controller.py +++ b/kdb-bot/src/bot_api/controller/discord/server_controller.py @@ -57,3 +57,9 @@ class ServerController: result = await self._discord_service.get_filtered_servers_async(dto) result.result = result.result.select(lambda x: x.to_dict()) return jsonify(result.to_dict()) + + @Route.get(f'{BasePath}/get/') + @Route.authorize + async def get_server_by_id(self, id: int) -> Response: + result = await self._discord_service.get_server_by_id_async(id) + return jsonify(result.to_dict()) diff --git a/kdb-bot/src/bot_api/service/discord_service.py b/kdb-bot/src/bot_api/service/discord_service.py index a0163dcc..7266e1bd 100644 --- a/kdb-bot/src/bot_api/service/discord_service.py +++ b/kdb-bot/src/bot_api/service/discord_service.py @@ -89,3 +89,10 @@ class DiscordService: List(ServerDTO, result), filtered_result.total_count ) + + async def get_server_by_id_async(self, id: int) -> ServerDTO: + server = self._servers.get_server_by_id(id) + guild = self._bot.get_guild(server.discord_server_id) + + server_dto = ServerTransformer.to_dto(server, guild.name, guild.member_count, guild.icon) + return server_dto diff --git a/kdb-web/src/app/app-routing.module.ts b/kdb-web/src/app/app-routing.module.ts index 803deb70..1a1c9c3b 100644 --- a/kdb-web/src/app/app-routing.module.ts +++ b/kdb-web/src/app/app-routing.module.ts @@ -7,6 +7,7 @@ import { AuthGuard } from './modules/shared/guards/auth/auth.guard'; const routes: Routes = [ { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, { path: 'dashboard', loadChildren: () => import('./modules/view/dashboard/dashboard.module').then(m => m.DashboardModule), canActivate: [AuthGuard] }, + { path: 'server', loadChildren: () => import('./modules/view/server/server.module').then(m => m.ServerModule), canActivate: [AuthGuard] }, { path: 'change-password', loadChildren: () => import('./modules/view/change-password/change-password.module').then(m => m.ChangePasswordModule), canActivate: [AuthGuard] }, { path: 'user-settings', loadChildren: () => import('./modules/view/user-settings/user-settings.module').then(m => m.UserSettingsModule), canActivate: [AuthGuard] }, { path: 'auth', loadChildren: () => import('./modules/auth/auth.module').then(m => m.AuthModule) }, diff --git a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html index c7dd5fdf..ded53310 100644 --- a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html +++ b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.html @@ -26,7 +26,7 @@
-
+
diff --git a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts index 0fb3ff19..d149298e 100644 --- a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts +++ b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; -import { FormGroup, FormControl, FormBuilder } from '@angular/forms'; +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'; @@ -17,7 +18,6 @@ import { ToastService } from 'src/app/services/toast/toast.service'; }) export class DashboardComponent implements OnInit { - loading = true; servers: ServerDTO[] = []; searchCriterions: ServerSelectCriterion = { @@ -40,10 +40,12 @@ export class DashboardComponent implements OnInit { private toastService: ToastService, private confirmDialog: ConfirmationDialogService, private fb: FormBuilder, - private translate: TranslateService + private translate: TranslateService, + private router: Router ) { } ngOnInit(): void { + this.spinnerService.showSpinner(); this.setFilterForm(); this.loadNextPage(); } @@ -73,13 +75,14 @@ export class DashboardComponent implements OnInit { } loadNextPage() { + this.spinnerService.showSpinner(); this.data.getFilteredServers(this.searchCriterions).pipe(catchError(err => { - this.loading = false; + this.spinnerService.hideSpinner(); return throwError(() => err); })).subscribe(list => { this.totalRecords = list.totalCount; this.servers = list.servers; - this.loading = false; + this.spinnerService.hideSpinner(); }); } @@ -102,4 +105,8 @@ export class DashboardComponent implements OnInit { this.filterForm.reset(); } + selectServer(server: ServerDTO) { + this.router.navigate(['/server', server.serverId]); + } + } diff --git a/kdb-web/src/app/modules/view/dashboard/dashboard-routing.module.ts b/kdb-web/src/app/modules/view/dashboard/dashboard-routing.module.ts index 27f77246..37add741 100644 --- a/kdb-web/src/app/modules/view/dashboard/dashboard-routing.module.ts +++ b/kdb-web/src/app/modules/view/dashboard/dashboard-routing.module.ts @@ -3,7 +3,7 @@ import { RouterModule, Routes } from '@angular/router'; import { DashboardComponent } from './components/dashboard/dashboard.component'; const routes: Routes = [ - {path: '', component: DashboardComponent} + { path: '', component: DashboardComponent } ]; @NgModule({ diff --git a/kdb-web/src/app/modules/view/dashboard/dashboard.module.ts b/kdb-web/src/app/modules/view/dashboard/dashboard.module.ts index 0b3abe11..80438b6e 100644 --- a/kdb-web/src/app/modules/view/dashboard/dashboard.module.ts +++ b/kdb-web/src/app/modules/view/dashboard/dashboard.module.ts @@ -1,9 +1,9 @@ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; -import { DashboardRoutingModule } from './dashboard-routing.module'; -import { DashboardComponent } from './components/dashboard/dashboard.component'; import { SharedModule } from '../../shared/shared.module'; +import { DashboardComponent } from './components/dashboard/dashboard.component'; +import { DashboardRoutingModule } from './dashboard-routing.module'; @NgModule({ diff --git a/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.html b/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.html new file mode 100644 index 00000000..1fbf61d3 --- /dev/null +++ b/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.html @@ -0,0 +1,36 @@ +

+ {{'view.dashboard.header' | translate}} +

+
+
+

+ + {{'view.dashboard.server.header' | translate}} +

+
+ +
+
+
+
+ + +
+

+ {{server.name}} +

+ +
+ + {{server.memberCount}} + {{'view.dashboard.server.member_count' | translate}} +
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.scss b/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.spec.ts b/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.spec.ts new file mode 100644 index 00000000..9f49b43a --- /dev/null +++ b/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ServerDashboardComponent } from './server-dashboard.component'; + +describe('ServerDashboardComponent', () => { + let component: ServerDashboardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ServerDashboardComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ServerDashboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.ts b/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.ts new file mode 100644 index 00000000..540ac8b6 --- /dev/null +++ b/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.ts @@ -0,0 +1,45 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { catchError, throwError } from 'rxjs'; +import { ServerDTO } from 'src/app/models/discord/server.dto'; +import { DataService } from 'src/app/services/data/data.service'; +import { SpinnerService } from 'src/app/services/spinner/spinner.service'; + +@Component({ + selector: 'app-server-dashboard', + templateUrl: './server-dashboard.component.html', + styleUrls: ['./server-dashboard.component.scss'] +}) +export class ServerDashboardComponent implements OnInit { + + id!: number; + server!: ServerDTO; + + constructor( + private route: ActivatedRoute, + private router: Router, + private data: DataService, + private spinner: SpinnerService + ) { } + + ngOnInit(): void { + this.route.params.subscribe(params => { + this.id = +params['id']; + + if (!this.id) { + this.router.navigate(['/dashboard']); + return; + } + + this.spinner.showSpinner(); + this.data.getServerByID(this.id).pipe(catchError(err => { + this.spinner.hideSpinner(); + return throwError(() => err); + })).subscribe(server => { + this.server = server; + this.spinner.hideSpinner(); + }); + }); + } + +} diff --git a/kdb-web/src/app/modules/view/server/server-routing.module.ts b/kdb-web/src/app/modules/view/server/server-routing.module.ts new file mode 100644 index 00000000..063beab1 --- /dev/null +++ b/kdb-web/src/app/modules/view/server/server-routing.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { ServerDashboardComponent } from './server-dashboard/server-dashboard.component'; + +const routes: Routes = [ + { path: '', component: ServerDashboardComponent }, + { path: ':id', component: ServerDashboardComponent } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ServerRoutingModule { } diff --git a/kdb-web/src/app/modules/view/server/server.module.ts b/kdb-web/src/app/modules/view/server/server.module.ts new file mode 100644 index 00000000..824e2412 --- /dev/null +++ b/kdb-web/src/app/modules/view/server/server.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ServerDashboardComponent } from './server-dashboard/server-dashboard.component'; +import { ServerRoutingModule } from './server-routing.module'; +import { SharedModule } from '../../shared/shared.module'; + + + +@NgModule({ + declarations: [ + ServerDashboardComponent + ], + imports: [ + CommonModule, + ServerRoutingModule, + SharedModule + ] +}) +export class ServerModule { } diff --git a/kdb-web/src/app/services/data/data.service.ts b/kdb-web/src/app/services/data/data.service.ts index 80a60600..6b1a0f76 100644 --- a/kdb-web/src/app/services/data/data.service.ts +++ b/kdb-web/src/app/services/data/data.service.ts @@ -42,4 +42,12 @@ export class DataService { }) }); } + + getServerByID(id: number): Observable { + return this.http.get(`${this.appsettings.getApiURL()}/api/discord/server/get/${id}`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } } diff --git a/kdb-web/src/assets/i18n/de.json b/kdb-web/src/assets/i18n/de.json index 83fd1ae3..96fa7213 100644 --- a/kdb-web/src/assets/i18n/de.json +++ b/kdb-web/src/assets/i18n/de.json @@ -7,10 +7,8 @@ }, "sidebar": { "dashboard": "Dashboard", - "domain_list": "Domänen", - "host_list": "Rechner", - "user_list": "Benutzer", - "login_list": "Logins", + "server": "Server", + "server_empty": "Kein Server ausgewählt", "config": "Konfiguration", "auth_user_list": "Benutzer" }, @@ -118,6 +116,9 @@ "name": "Name" } }, + "server": { + "header": "Server" + }, "user-list": {}, "change-password": { "header": "Passwort ändern", From c094a3efae3a05ff82e4fdd45cac4d7db5a356ff Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 18 Oct 2022 12:41:42 +0200 Subject: [PATCH 044/275] Improved get server logic #72 --- .../src/bot_api/service/discord_service.py | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/kdb-bot/src/bot_api/service/discord_service.py b/kdb-bot/src/bot_api/service/discord_service.py index 7266e1bd..304e340a 100644 --- a/kdb-bot/src/bot_api/service/discord_service.py +++ b/kdb-bot/src/bot_api/service/discord_service.py @@ -15,6 +15,7 @@ from bot_api.transformer.server_transformer import ServerTransformer from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.model.auth_role_enum import AuthRoleEnum +from bot_data.model.server import Server class DiscordService: @@ -31,14 +32,26 @@ class DiscordService: self._auth = auth self._users = users + def _to_dto(self, x: Server) -> Optional[ServerDTO]: + guild = self._bot.get_guild(x.discord_server_id) + if guild is None: + return ServerTransformer.to_dto( + x, + '', + 0, + None + ) + + return ServerTransformer.to_dto( + x, + guild.name, + guild.member_count, + guild.icon + ) + async def get_all_servers(self) -> List[ServerDTO]: - servers = self._servers.get_servers() - return servers.select(lambda x: ServerTransformer.to_dto( - x, - self._bot.get_guild(x.discord_server_id).name, - self._bot.get_guild(x.discord_server_id).member_count, - self._bot.get_guild(x.discord_server_id).icon - )) + servers = List(ServerDTO, self._servers.get_servers()) + return servers.select(self._to_dto).where(lambda x: x.name != '') async def get_all_servers_by_user(self) -> List[ServerDTO]: token = self._auth.get_decoded_token_from_request() @@ -53,12 +66,8 @@ class DiscordService: user_from_db = self._users.find_user_by_id(0 if user.user_id is None else user.user_id) servers = self._servers.get_servers().where(lambda x: user_from_db is not None and x.server_id == user_from_db.server.server_id) - return servers.select(lambda x: ServerTransformer.to_dto( - x, - self._bot.get_guild(x.discord_server_id).name, - self._bot.get_guild(x.discord_server_id).member_count, - self._bot.get_guild(x.discord_server_id).icon - )) + servers = List(ServerDTO, servers) + return servers.select(self._to_dto).where(lambda x: x.name != '') async def get_filtered_servers_async(self, criteria: ServerSelectCriteria) -> ServerFilteredResultDTO: token = self._auth.get_decoded_token_from_request() @@ -67,27 +76,21 @@ class DiscordService: role = AuthRoleEnum(token['role']) filtered_result = self._servers.get_filtered_servers(criteria) - servers = filtered_result.result + # filter out servers, where the user not exists if role != AuthRoleEnum.admin: user = await self._auth.find_auth_user_by_email_async(token['email']) user_from_db = self._users.find_user_by_id(0 if user.user_id is None else user.user_id) - servers = servers.where(lambda x: user_from_db is not None and x.server_id == user_from_db.server.server_id) + filtered_result.result = filtered_result.result.where(lambda x: user_from_db is not None and x.server_id == user_from_db.server.server_id) - result = servers.select( - lambda x: ServerTransformer.to_dto( - x, - self._bot.get_guild(x.discord_server_id).name, - self._bot.get_guild(x.discord_server_id).member_count, - self._bot.get_guild(x.discord_server_id).icon - ) - ) + servers: List = filtered_result.result.select(self._to_dto).where(lambda x: x.name != '') + result = List(ServerDTO, servers) if criteria.name is not None and criteria.name != '': result = result.where(lambda x: criteria.name.lower() in x.name.lower() or x.name.lower() == criteria.name.lower()) return ServerFilteredResultDTO( List(ServerDTO, result), - filtered_result.total_count + servers.count() ) async def get_server_by_id_async(self, id: int) -> ServerDTO: From 1055d5c2e19f632da0916618158498c05426712f Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 18 Oct 2022 13:44:13 +0200 Subject: [PATCH 045/275] Improved design of menu to handle servers #72 --- kdb-web/.prettierrc | 4 +++ .../components/sidebar/sidebar.component.html | 2 +- .../components/sidebar/sidebar.component.ts | 16 ++++++++-- .../src/app/modules/shared/shared.module.ts | 5 ++- .../src/app/services/theme/theme.service.ts | 2 +- kdb-web/src/assets/i18n/de.json | 25 ++++----------- kdb-web/src/styles/primeng-fixes.scss | 32 +++++++++++++++++-- .../styles/themes/sh-edraft-dark-theme.scss | 25 ++++++++++++--- 8 files changed, 79 insertions(+), 32 deletions(-) create mode 100644 kdb-web/.prettierrc diff --git a/kdb-web/.prettierrc b/kdb-web/.prettierrc new file mode 100644 index 00000000..5a938ce1 --- /dev/null +++ b/kdb-web/.prettierrc @@ -0,0 +1,4 @@ +{ + "tabWidth": 4, + "useTabs": false +} diff --git a/kdb-web/src/app/components/sidebar/sidebar.component.html b/kdb-web/src/app/components/sidebar/sidebar.component.html index b3bb4f3b..6f72d17e 100644 --- a/kdb-web/src/app/components/sidebar/sidebar.component.html +++ b/kdb-web/src/app/components/sidebar/sidebar.component.html @@ -1,3 +1,3 @@ \ No newline at end of file diff --git a/kdb-web/src/app/components/sidebar/sidebar.component.ts b/kdb-web/src/app/components/sidebar/sidebar.component.ts index cbf13c6f..f7815fbd 100644 --- a/kdb-web/src/app/components/sidebar/sidebar.component.ts +++ b/kdb-web/src/app/components/sidebar/sidebar.component.ts @@ -40,6 +40,13 @@ export class SidebarComponent implements OnInit { this.menuItems = []; this.menuItems = [ { label: this.isSidebarOpen ? this.translateService.instant('sidebar.dashboard') : '', icon: 'pi pi-th-large', routerLink: 'dashboard' }, + + { + label: this.isSidebarOpen ? this.translateService.instant('sidebar.server') : '', icon: 'pi pi-server', items: [ + { label: this.isSidebarOpen ? this.translateService.instant('sidebar.settings') : '', icon: 'pi pi-cog', routerLink: 'server/id/settings' }, + { label: this.isSidebarOpen ? this.translateService.instant('sidebar.members') : '', icon: 'pi pi-user-edit', routerLink: 'server/id/members' }, + ] + }, ]; if (!hasPermission) { @@ -47,9 +54,12 @@ export class SidebarComponent implements OnInit { } this.menuItems.push( - { separator: true }, - { label: this.isSidebarOpen ? this.translateService.instant('sidebar.config') : '', icon: 'pi pi-cog', routerLink: '/admin/settings' }, - { label: this.isSidebarOpen ? this.translateService.instant('sidebar.auth_user_list') : '', icon: 'pi pi-user-edit', routerLink: '/admin/users' }, + { + label: this.isSidebarOpen ? this.translateService.instant('sidebar.administration') : '', icon: 'pi pi-cog', items: [ + { label: this.isSidebarOpen ? this.translateService.instant('sidebar.config') : '', icon: 'pi pi-cog', routerLink: '/admin/settings' }, + { label: this.isSidebarOpen ? this.translateService.instant('sidebar.auth_user_list') : '', icon: 'pi pi-user-edit', routerLink: '/admin/users' }, + ] + }, ); this.menuItems = this.menuItems.slice(); }); diff --git a/kdb-web/src/app/modules/shared/shared.module.ts b/kdb-web/src/app/modules/shared/shared.module.ts index 9a77a513..59c0d0ef 100644 --- a/kdb-web/src/app/modules/shared/shared.module.ts +++ b/kdb-web/src/app/modules/shared/shared.module.ts @@ -18,6 +18,7 @@ import { ToastModule } from 'primeng/toast'; import { AuthRolePipe } from './pipes/auth-role.pipe'; import { IpAddressPipe } from './pipes/ip-address.pipe'; import { BoolPipe } from './pipes/bool.pipe'; +import { PanelMenuModule } from 'primeng/panelmenu'; @@ -44,7 +45,8 @@ import { BoolPipe } from './pipes/bool.pipe'; CheckboxModule, DropdownModule, TranslateModule, - DynamicDialogModule + DynamicDialogModule, + PanelMenuModule, ], exports: [ ButtonModule, @@ -63,6 +65,7 @@ import { BoolPipe } from './pipes/bool.pipe'; DropdownModule, TranslateModule, DynamicDialogModule, + PanelMenuModule, AuthRolePipe, IpAddressPipe, BoolPipe, diff --git a/kdb-web/src/app/services/theme/theme.service.ts b/kdb-web/src/app/services/theme/theme.service.ts index 914ba774..6dc3410a 100644 --- a/kdb-web/src/app/services/theme/theme.service.ts +++ b/kdb-web/src/app/services/theme/theme.service.ts @@ -27,7 +27,7 @@ export class ThemeService { }); this.isSidebarOpen$.subscribe(isSidebarOpen => { this.isSidebarOpen = isSidebarOpen; - this.sidebarWidth$.next(isSidebarOpen ? '150px' : '50px'); + this.sidebarWidth$.next(isSidebarOpen ? '175px' : '75px'); }); this.sidebarWidth$.subscribe(sidebarWidth => { this.sidebarWidth = sidebarWidth; diff --git a/kdb-web/src/assets/i18n/de.json b/kdb-web/src/assets/i18n/de.json index 96fa7213..9eec0bf3 100644 --- a/kdb-web/src/assets/i18n/de.json +++ b/kdb-web/src/assets/i18n/de.json @@ -9,6 +9,9 @@ "dashboard": "Dashboard", "server": "Server", "server_empty": "Kein Server ausgewählt", + "settings": "Einstellungen", + "members": "Mitglieder", + "administration": "Administration", "config": "Konfiguration", "auth_user_list": "Benutzer" }, @@ -212,24 +215,8 @@ "Freitag", "Samstag" ], - "dayNamesShort": [ - "Son", - "Mon", - "Die", - "Mit", - "Don", - "Fre", - "Sam" - ], - "dayNamesMin": [ - "So", - "Mo", - "Di", - "Mi", - "Do", - "Fr", - "Sa" - ], + "dayNamesShort": ["Son", "Mon", "Die", "Mit", "Don", "Fre", "Sam"], + "dayNamesMin": ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"], "monthNames": [ "Januar", "Februar", @@ -267,4 +254,4 @@ "emptyMessage": "Keine Ergebnisse gefunden", "emptyFilterMessage": "Keine Ergebnisse gefunden" } -} \ No newline at end of file +} diff --git a/kdb-web/src/styles/primeng-fixes.scss b/kdb-web/src/styles/primeng-fixes.scss index 560ad4e3..5bad03ee 100644 --- a/kdb-web/src/styles/primeng-fixes.scss +++ b/kdb-web/src/styles/primeng-fixes.scss @@ -6,14 +6,17 @@ } } -.p-menu { +.p-menu, +.p-panelmenu { background: none !important; border: none !important; width: auto !important; border-radius: 0px !important; padding: 0 !important; - .p-menuitem-link { + .p-menuitem-link, + .p-panelmenu-header > a, + .p-panelmenu-content .p-menuitem .p-menuitem-link { $distance: 10px; padding: $distance 0px $distance $distance !important; margin: 4px 0px 4px 6px !important; @@ -24,6 +27,31 @@ top: $headerHeight !important; } +.p-panelmenu { + .p-panelmenu-icon { + order: 1; // to be the first item on right side. + } + .p-menuitem-text { + flex-grow: 1; // to fill the whole space and push the icon to the end + } + + .p-panelmenu-header > a { + border: none !important; + border-radius: none !important; + font-weight: none !important; + transition: none !important; + } + + .p-panelmenu-content { + border: none !important; + background: none !important; + } + + .p-menuitem-text { + line-height: normal !important; + } +} + ui-menu .ui-menu-parent .ui-menu-child { width: 400px; /* exagerated !! */ } diff --git a/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss b/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss index 8fa192f1..6e0d8eb0 100644 --- a/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss +++ b/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss @@ -252,28 +252,43 @@ stroke: $primaryHeaderColor !important; } - .p-menu { + .p-menu, + .p-panelmenu { color: $primaryTextColor !important; .p-menuitem-link .p-menuitem-text, - .p-menuitem-link .p-menuitem-icon { + .p-menuitem-link .p-menuitem-icon, + .p-panelmenu-header > a { color: $primaryTextColor !important; + background: transparent !important; + font-size: 1rem !important; + font-weight: normal !important; } - .p-menuitem-link:focus { + .p-menuitem-link:focus, + .p-panelmenu-header > a:focus, + .p-panelmenu-content .p-menuitem .p-menuitem-link:focus { box-shadow: none !important; } - .p-menuitem-link:hover { + .p-menuitem-link:hover, + .p-panelmenu-header > a:hover, + .p-panelmenu-content .p-menuitem .p-menuitem-link:hover { background-color: $secondaryBackgroundColor !important; $border-radius: 20px; border-radius: $border-radius 0px 0px $border-radius; .p-menuitem-text, - .p-menuitem-icon { + .p-menuitem-icon, + .p-menuitem-text, + .p-panelmenu-icon { color: $primaryHeaderColor !important; } } + + .p-panelmenu-content { + margin: 5px 0px 5px 10px; + } } .p-menu-overlay { From 7760ee57254c8a1f409ab01d40509da3ea5e424c Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 18 Oct 2022 16:23:40 +0200 Subject: [PATCH 046/275] Update menu when server is selected #72 --- .../components/sidebar/sidebar.component.ts | 64 +++++++++++++------ .../dashboard/dashboard.component.ts | 7 +- .../server-dashboard.component.ts | 29 ++++----- .../view/server/server-routing.module.ts | 3 +- .../app/services/data/server.service.spec.ts | 16 +++++ .../src/app/services/data/server.service.ts | 23 +++++++ 6 files changed, 100 insertions(+), 42 deletions(-) create mode 100644 kdb-web/src/app/services/data/server.service.spec.ts create mode 100644 kdb-web/src/app/services/data/server.service.ts diff --git a/kdb-web/src/app/components/sidebar/sidebar.component.ts b/kdb-web/src/app/components/sidebar/sidebar.component.ts index f7815fbd..d43b42e5 100644 --- a/kdb-web/src/app/components/sidebar/sidebar.component.ts +++ b/kdb-web/src/app/components/sidebar/sidebar.component.ts @@ -1,8 +1,10 @@ import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; import { LangChangeEvent, TranslateService } from '@ngx-translate/core'; import { MenuItem } from 'primeng/api'; import { AuthRoles } from 'src/app/models/auth/auth-roles.enum'; import { AuthService } from 'src/app/services/auth/auth.service'; +import { ServerService } from 'src/app/services/data/server.service'; import { ThemeService } from 'src/app/services/theme/theme.service'; @Component({ @@ -13,13 +15,16 @@ import { ThemeService } from 'src/app/services/theme/theme.service'; export class SidebarComponent implements OnInit { isSidebarOpen: boolean = true; - menuItems!: MenuItem[]; + private serverId!: number; + constructor( private authService: AuthService, private translateService: TranslateService, - private themeService: ThemeService + private themeService: ThemeService, + private route: ActivatedRoute, + private serverService: ServerService ) { this.themeService.isSidebarOpen$.subscribe(value => { this.isSidebarOpen = value; @@ -29,6 +34,15 @@ export class SidebarComponent implements OnInit { this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { this.setMenu(); }); + + this.serverService.server$.subscribe(server => { + if (!server) { + return; + } + + this.serverId = server.serverId; + this.setMenu(); + }); } ngOnInit(): void { @@ -39,30 +53,40 @@ export class SidebarComponent implements OnInit { this.authService.hasUserPermission(AuthRoles.Admin).then(hasPermission => { this.menuItems = []; this.menuItems = [ - { label: this.isSidebarOpen ? this.translateService.instant('sidebar.dashboard') : '', icon: 'pi pi-th-large', routerLink: 'dashboard' }, - - { - label: this.isSidebarOpen ? this.translateService.instant('sidebar.server') : '', icon: 'pi pi-server', items: [ - { label: this.isSidebarOpen ? this.translateService.instant('sidebar.settings') : '', icon: 'pi pi-cog', routerLink: 'server/id/settings' }, - { label: this.isSidebarOpen ? this.translateService.instant('sidebar.members') : '', icon: 'pi pi-user-edit', routerLink: 'server/id/members' }, - ] - }, + { label: this.isSidebarOpen ? this.translateService.instant('sidebar.dashboard') : '', icon: 'pi pi-th-large', routerLink: 'dashboard' } ]; - if (!hasPermission) { - return; + if (this.serverId) { + this.addServerMenu(); } - this.menuItems.push( - { - label: this.isSidebarOpen ? this.translateService.instant('sidebar.administration') : '', icon: 'pi pi-cog', items: [ - { label: this.isSidebarOpen ? this.translateService.instant('sidebar.config') : '', icon: 'pi pi-cog', routerLink: '/admin/settings' }, - { label: this.isSidebarOpen ? this.translateService.instant('sidebar.auth_user_list') : '', icon: 'pi pi-user-edit', routerLink: '/admin/users' }, - ] - }, - ); + if (hasPermission) { + this.addAdminMenu(); + } this.menuItems = this.menuItems.slice(); }); } + addServerMenu() { + this.menuItems.push( + { + label: this.isSidebarOpen ? this.translateService.instant('sidebar.server') : '', icon: 'pi pi-server', items: [ + { label: this.isSidebarOpen ? this.translateService.instant('sidebar.settings') : '', icon: 'pi pi-cog', routerLink: 'server/settings' }, + { label: this.isSidebarOpen ? this.translateService.instant('sidebar.members') : '', icon: 'pi pi-users', routerLink: 'server/members' }, + ] + } + ); + } + + addAdminMenu() { + this.menuItems.push( + { + label: this.isSidebarOpen ? this.translateService.instant('sidebar.administration') : '', icon: 'pi pi-cog', items: [ + { label: this.isSidebarOpen ? this.translateService.instant('sidebar.config') : '', icon: 'pi pi-cog', routerLink: '/admin/settings' }, + { label: this.isSidebarOpen ? this.translateService.instant('sidebar.auth_user_list') : '', icon: 'pi pi-user-edit', routerLink: '/admin/users' }, + ] + }, + ); + } + } diff --git a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts index d149298e..046e00c6 100644 --- a/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts +++ b/kdb-web/src/app/modules/view/dashboard/components/dashboard/dashboard.component.ts @@ -8,6 +8,7 @@ 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'; @@ -41,7 +42,8 @@ export class DashboardComponent implements OnInit { private confirmDialog: ConfirmationDialogService, private fb: FormBuilder, private translate: TranslateService, - private router: Router + private router: Router, + private serverService: ServerService ) { } ngOnInit(): void { @@ -106,7 +108,8 @@ export class DashboardComponent implements OnInit { } selectServer(server: ServerDTO) { - this.router.navigate(['/server', server.serverId]); + this.serverService.server$.next(server); + this.router.navigate(['/server']); } } diff --git a/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.ts b/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.ts index 540ac8b6..158c7711 100644 --- a/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.ts +++ b/kdb-web/src/app/modules/view/server/server-dashboard/server-dashboard.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { catchError, throwError } from 'rxjs'; 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'; @Component({ @@ -19,27 +19,20 @@ export class ServerDashboardComponent implements OnInit { private route: ActivatedRoute, private router: Router, private data: DataService, - private spinner: SpinnerService + private spinner: SpinnerService, + private serverService: ServerService ) { } ngOnInit(): void { - this.route.params.subscribe(params => { - this.id = +params['id']; + this.spinner.showSpinner(); + if (!this.serverService.server$.value) { + this.spinner.hideSpinner(); + this.router.navigate(['/dashboard']); + return; + } - if (!this.id) { - this.router.navigate(['/dashboard']); - return; - } - - this.spinner.showSpinner(); - this.data.getServerByID(this.id).pipe(catchError(err => { - this.spinner.hideSpinner(); - return throwError(() => err); - })).subscribe(server => { - this.server = server; - this.spinner.hideSpinner(); - }); - }); + this.server = this.serverService.server$.value; + this.spinner.hideSpinner(); } } diff --git a/kdb-web/src/app/modules/view/server/server-routing.module.ts b/kdb-web/src/app/modules/view/server/server-routing.module.ts index 063beab1..7b9f3ee7 100644 --- a/kdb-web/src/app/modules/view/server/server-routing.module.ts +++ b/kdb-web/src/app/modules/view/server/server-routing.module.ts @@ -3,8 +3,7 @@ import { RouterModule, Routes } from '@angular/router'; import { ServerDashboardComponent } from './server-dashboard/server-dashboard.component'; const routes: Routes = [ - { path: '', component: ServerDashboardComponent }, - { path: ':id', component: ServerDashboardComponent } + { path: '', component: ServerDashboardComponent } ]; @NgModule({ diff --git a/kdb-web/src/app/services/data/server.service.spec.ts b/kdb-web/src/app/services/data/server.service.spec.ts new file mode 100644 index 00000000..906c1601 --- /dev/null +++ b/kdb-web/src/app/services/data/server.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ServerService } from './server.service'; + +describe('ServerService', () => { + let service: ServerService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ServerService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/services/data/server.service.ts b/kdb-web/src/app/services/data/server.service.ts new file mode 100644 index 00000000..05d8673d --- /dev/null +++ b/kdb-web/src/app/services/data/server.service.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { ServerDTO } from 'src/app/models/discord/server.dto'; + +@Injectable({ + providedIn: 'root' +}) +export class ServerService { + + private server!: ServerDTO; + server$ = new BehaviorSubject(null); + + constructor() { + this.server$.subscribe(server => { + if (!server) { + return; + } + this.server = server; + }); + } + + +} From dee8e4fe74280c7594699bf5a1962522f76a8b8a Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 18 Oct 2022 16:36:24 +0200 Subject: [PATCH 047/275] Fixed themes #72 --- .../src/styles/themes/default-dark-theme.scss | 68 ++++++++++++++---- .../styles/themes/default-light-theme.scss | 70 +++++++++++++++---- .../styles/themes/sh-edraft-dark-theme.scss | 2 +- .../styles/themes/sh-edraft-light-theme.scss | 68 ++++++++++++++---- 4 files changed, 166 insertions(+), 42 deletions(-) diff --git a/kdb-web/src/styles/themes/default-dark-theme.scss b/kdb-web/src/styles/themes/default-dark-theme.scss index 1a3bd5d1..8a5b60fa 100644 --- a/kdb-web/src/styles/themes/default-dark-theme.scss +++ b/kdb-web/src/styles/themes/default-dark-theme.scss @@ -15,16 +15,9 @@ $primaryErrorColor: #b00020; $secondaryErrorColor: #e94948; - $default-border: 1px solid $secondaryBackgroundColor3; + $default-border: 2px solid $secondaryBackgroundColor3; - background-color: $primaryBackgroundColor !important; - - html, - body { - margin: 0; - - font-size: 16px; - } + background-color: $primaryBackgroundColor; h1, h2 { @@ -122,6 +115,35 @@ .content-divider { border-bottom: $default-border; } + + .server-list-wrapper { + .server-filter { + } + + .server-count { + } + + .server-list { + .server { + border: $default-border; + border-radius: 15px; + + .logo { + img { + border-radius: 100%; + } + } + + .name { + color: $primaryHeaderColor; + } + + &:hover { + border-color: $primaryHeaderColor !important; + } + } + } + } } } } @@ -228,28 +250,43 @@ stroke: $primaryHeaderColor !important; } - .p-menu { + .p-menu, + .p-panelmenu { color: $primaryTextColor !important; .p-menuitem-link .p-menuitem-text, - .p-menuitem-link .p-menuitem-icon { + .p-menuitem-link .p-menuitem-icon, + .p-panelmenu-header > a { color: $primaryTextColor !important; + background: transparent !important; + font-size: 1rem !important; + font-weight: normal !important; } - .p-menuitem-link:focus { + .p-menuitem-link:focus, + .p-panelmenu-header > a:focus, + .p-panelmenu-content .p-menuitem .p-menuitem-link:focus { box-shadow: none !important; } - .p-menuitem-link:hover { + .p-menuitem-link:hover, + .p-panelmenu-header > a:hover, + .p-panelmenu-content .p-menuitem .p-menuitem-link:hover { background-color: $secondaryBackgroundColor !important; $border-radius: 20px; border-radius: $border-radius 0px 0px $border-radius; .p-menuitem-text, - .p-menuitem-icon { + .p-menuitem-icon, + .p-menuitem-text, + .p-panelmenu-icon { color: $primaryHeaderColor !important; } } + + .p-panelmenu-content { + margin: 5px 0px 5px 10px; + } } .p-menu-overlay { @@ -388,6 +425,9 @@ input, .p-password { + border-radius: 10px; + border: $default-border; + &:focus { box-shadow: none !important; } diff --git a/kdb-web/src/styles/themes/default-light-theme.scss b/kdb-web/src/styles/themes/default-light-theme.scss index 5993a058..bc58a6f7 100644 --- a/kdb-web/src/styles/themes/default-light-theme.scss +++ b/kdb-web/src/styles/themes/default-light-theme.scss @@ -15,14 +15,9 @@ $primaryErrorColor: #b00020; $secondaryErrorColor: #e94948; - $default-border: 1px solid $secondaryBackgroundColor; + $default-border: 2px solid $secondaryBackgroundColor; - html, - body { - margin: 0; - - font-size: 16px; - } + background-color: $primaryBackgroundColor; h1, h2 { @@ -120,6 +115,35 @@ .content-divider { border-bottom: $default-border; } + + .server-list-wrapper { + .server-filter { + } + + .server-count { + } + + .server-list { + .server { + border: $default-border; + border-radius: 15px; + + .logo { + img { + border-radius: 100%; + } + } + + .name { + color: $primaryHeaderColor; + } + + &:hover { + border-color: $primaryHeaderColor !important; + } + } + } + } } } } @@ -226,28 +250,43 @@ stroke: $primaryHeaderColor !important; } - .p-menu { + .p-menu, + .p-panelmenu { color: $primaryTextColor !important; .p-menuitem-link .p-menuitem-text, - .p-menuitem-link .p-menuitem-icon { + .p-menuitem-link .p-menuitem-icon, + .p-panelmenu-header > a { color: $primaryTextColor !important; + background: transparent !important; + font-size: 1rem !important; + font-weight: normal !important; } - .p-menuitem-link:focus { + .p-menuitem-link:focus, + .p-panelmenu-header > a:focus, + .p-panelmenu-content .p-menuitem .p-menuitem-link:focus { box-shadow: none !important; } - .p-menuitem-link:hover { + .p-menuitem-link:hover, + .p-panelmenu-header > a:hover, + .p-panelmenu-content .p-menuitem .p-menuitem-link:hover { background-color: $secondaryBackgroundColor !important; $border-radius: 20px; border-radius: $border-radius 0px 0px $border-radius; - + .p-menuitem-text, - .p-menuitem-icon { + .p-menuitem-icon, + .p-menuitem-text, + .p-panelmenu-icon { color: $primaryHeaderColor !important; } } + + .p-panelmenu-content { + margin: 5px 0px 5px 10px; + } } .p-menu-overlay { @@ -386,6 +425,9 @@ input, .p-password { + border-radius: 10px; + border: $default-border; + &:focus { box-shadow: none !important; } @@ -430,7 +472,7 @@ color: $primaryTextColor !important; border: 0 !important; padding: 0px !important; - + &:hover { background-color: transparent !important; color: $primaryHeaderColor !important; diff --git a/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss b/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss index 6e0d8eb0..9e45019f 100644 --- a/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss +++ b/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss @@ -17,7 +17,7 @@ $default-border: 2px solid $secondaryBackgroundColor3; - background-color: $primaryBackgroundColor !important; + background-color: $primaryBackgroundColor; h1, h2 { diff --git a/kdb-web/src/styles/themes/sh-edraft-light-theme.scss b/kdb-web/src/styles/themes/sh-edraft-light-theme.scss index fdd80949..26c70cd5 100644 --- a/kdb-web/src/styles/themes/sh-edraft-light-theme.scss +++ b/kdb-web/src/styles/themes/sh-edraft-light-theme.scss @@ -15,14 +15,9 @@ $primaryErrorColor: #b00020; $secondaryErrorColor: #e94948; - $default-border: 1px solid $secondaryBackgroundColor; + $default-border: 2px solid $secondaryBackgroundColor; - html, - body { - margin: 0; - - font-size: 16px; - } + background-color: $primaryBackgroundColor; h1, h2 { @@ -121,6 +116,35 @@ border-bottom: $default-border; } } + + .server-list-wrapper { + .server-filter { + } + + .server-count { + } + + .server-list { + .server { + border: $default-border; + border-radius: 15px; + + .logo { + img { + border-radius: 100%; + } + } + + .name { + color: $primaryHeaderColor; + } + + &:hover { + border-color: $primaryHeaderColor !important; + } + } + } + } } } } @@ -226,28 +250,43 @@ stroke: $primaryHeaderColor !important; } - .p-menu { + .p-menu, + .p-panelmenu { color: $primaryTextColor !important; .p-menuitem-link .p-menuitem-text, - .p-menuitem-link .p-menuitem-icon { + .p-menuitem-link .p-menuitem-icon, + .p-panelmenu-header > a { color: $primaryTextColor !important; + background: transparent !important; + font-size: 1rem !important; + font-weight: normal !important; } - .p-menuitem-link:focus { + .p-menuitem-link:focus, + .p-panelmenu-header > a:focus, + .p-panelmenu-content .p-menuitem .p-menuitem-link:focus { box-shadow: none !important; } - .p-menuitem-link:hover { + .p-menuitem-link:hover, + .p-panelmenu-header > a:hover, + .p-panelmenu-content .p-menuitem .p-menuitem-link:hover { background-color: $secondaryBackgroundColor !important; $border-radius: 20px; border-radius: $border-radius 0px 0px $border-radius; .p-menuitem-text, - .p-menuitem-icon { + .p-menuitem-icon, + .p-menuitem-text, + .p-panelmenu-icon { color: $primaryHeaderColor !important; } } + + .p-panelmenu-content { + margin: 5px 0px 5px 10px; + } } .p-menu-overlay { @@ -386,6 +425,9 @@ input, .p-password { + border-radius: 10px; + border: $default-border; + &:focus { box-shadow: none !important; } @@ -430,7 +472,7 @@ color: $primaryTextColor !important; border: 0 !important; padding: 0px !important; - + &:hover { background-color: transparent !important; color: $primaryHeaderColor !important; From a51efa641d2cf70d2e57f6b8bb179e203a4c44fe Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 18 Oct 2022 18:04:53 +0200 Subject: [PATCH 048/275] Improved auth views and imrpoved default settings handling #70 --- kdb-web/.gitignore | 3 + kdb-web/package-lock.json | 607 +++++++++--------- kdb-web/src/app/app.component.ts | 32 +- .../app/components/header/header.component.ts | 1 - kdb-web/src/app/modules/auth/auth.module.ts | 8 +- .../auth-header/auth-header.component.html | 10 + .../auth-header/auth-header.component.scss | 0 .../auth-header/auth-header.component.spec.ts | 23 + .../auth-header/auth-header.component.ts | 83 +++ .../forget-password.component.html | 30 +- .../components/login/login.component.html | 22 +- .../registration/registration.component.html | 36 +- .../src/app/services/theme/theme.service.ts | 10 +- kdb-web/src/assets/i18n/de.json | 33 +- kdb-web/src/assets/i18n/en.json | 4 +- kdb-web/src/styles.scss | 16 +- kdb-web/src/styles/primeng-fixes.scss | 7 +- .../src/styles/themes/default-dark-theme.scss | 5 +- .../styles/themes/default-light-theme.scss | 5 +- .../styles/themes/sh-edraft-dark-theme.scss | 5 +- .../styles/themes/sh-edraft-light-theme.scss | 5 +- 21 files changed, 569 insertions(+), 376 deletions(-) create mode 100644 kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.html create mode 100644 kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.scss create mode 100644 kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.spec.ts create mode 100644 kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.ts diff --git a/kdb-web/.gitignore b/kdb-web/.gitignore index c03363f8..50db1a94 100644 --- a/kdb-web/.gitignore +++ b/kdb-web/.gitignore @@ -41,3 +41,6 @@ testem.log # System files .DS_Store Thumbs.db + +ignore +ignore/* \ No newline at end of file diff --git a/kdb-web/package-lock.json b/kdb-web/package-lock.json index 273efa03..6094a76e 100644 --- a/kdb-web/package-lock.json +++ b/kdb-web/package-lock.json @@ -64,12 +64,12 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1402.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.5.tgz", - "integrity": "sha512-vtJEwB51UEY1Q7FCI7xGLdhdb2SRTtI1Qs0or95momn85NuxlaMQsXK1Wxu9/EwtWKZK8dXePXbB/hpiNt61JQ==", + "version": "0.1402.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.6.tgz", + "integrity": "sha512-qTmPBD7fBXBtlSapGLUEcJvRuL/O556zCFFpH3kSlzPNTYxi2falBjGY+4aG+078RXT1vVZtFsvRTart6VbhAg==", "dev": true, "dependencies": { - "@angular-devkit/core": "14.2.5", + "@angular-devkit/core": "14.2.6", "rxjs": "6.6.7" }, "engines": { @@ -97,15 +97,15 @@ "dev": true }, "node_modules/@angular-devkit/build-angular": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.5.tgz", - "integrity": "sha512-jSgH11E+zs1C24lXj7R/PgXsTUpoYoMr1GtO6mpVROgXL5czVlL+b/B1p2HwbcAKuI9WXb48X6OZ6fOZhDQlSg==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.6.tgz", + "integrity": "sha512-XtaUwb3aZ8S0vl0y9bmbdFOH0KQCQ778twFH+ZfHW2BcPYtQz2Cy2rcVKXBQ850RyC0GxgMPfco6OGQndPpizg==", "dev": true, "dependencies": { "@ampproject/remapping": "2.2.0", - "@angular-devkit/architect": "0.1402.5", - "@angular-devkit/build-webpack": "0.1402.5", - "@angular-devkit/core": "14.2.5", + "@angular-devkit/architect": "0.1402.6", + "@angular-devkit/build-webpack": "0.1402.6", + "@angular-devkit/core": "14.2.6", "@babel/core": "7.18.10", "@babel/generator": "7.18.12", "@babel/helper-annotate-as-pure": "7.18.6", @@ -116,7 +116,7 @@ "@babel/runtime": "7.18.9", "@babel/template": "7.18.10", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "14.2.5", + "@ngtools/webpack": "14.2.6", "ansi-colors": "4.1.3", "babel-loader": "8.2.5", "babel-plugin-istanbul": "6.1.1", @@ -223,12 +223,12 @@ "dev": true }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1402.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.5.tgz", - "integrity": "sha512-h+o0GZD9iATwWjaTiUR0lJ3QZ9twUOJ1sotRchXHzAXMuaDk8wqqPriL5S0qDMlA2QqpNt4OD9rodUCRwae7fw==", + "version": "0.1402.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.6.tgz", + "integrity": "sha512-gKsDxQ9pze0N1qDM0kdM4FfwpkjSOb0bQzqjZi7wTfrh/WGIQMCjG9CRwWT+Z289ZKaTpcQDPsDtOSo5QpKNDg==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1402.5", + "@angular-devkit/architect": "0.1402.6", "rxjs": "6.6.7" }, "engines": { @@ -260,9 +260,9 @@ "dev": true }, "node_modules/@angular-devkit/core": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.5.tgz", - "integrity": "sha512-lSje+HX0fx9Y2A4k63jVHrWdGT4wellhwcZpTCv9P6LvdfTkAlrfra3TaYhUPjavCsPwlRC/VVQN3Qkzk5m6gA==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.6.tgz", + "integrity": "sha512-qtRSdRm/h7C3ya04PJTDgQXV6mM8Y4RakANX1GTSXetCf9AVSxg74NJX76DWUgiHT4JiPYnJgJU6Hr/L0H6JOQ==", "dev": true, "dependencies": { "ajv": "8.11.0", @@ -381,9 +381,9 @@ "dev": true }, "node_modules/@angular/animations": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.2.5.tgz", - "integrity": "sha512-4BhR9jSjgIwoK/alu7FSwSU5SxISMVFBAl/4cEYchfCqnflMNkZ8WwRVKTQjyeuYW5KtQTw9jRNp4tGK1YQWYw==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.2.6.tgz", + "integrity": "sha512-Tmb3Jj016j8m8OOGSk/ReL0b+OuUCMj0KQansk3C4pCpH9oPF67Vnm0fmVC2wYjjsSS/iDgl4kaDW740wfPGNQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -391,7 +391,7 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/core": "14.2.5" + "@angular/core": "14.2.6" } }, "node_modules/@angular/cli": { @@ -514,9 +514,9 @@ "dev": true }, "node_modules/@angular/common": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.2.5.tgz", - "integrity": "sha512-v2fIK6imfMkUvYNjZQO+drE39QO3eSS95Yy7UN+6inb47DkAfzx6hipA9zKrMENjsS3kDv1d7cgDHE7WuOCzIw==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.2.6.tgz", + "integrity": "sha512-WNX7xe8LKP5DHPlae+c77PDwj0iIAAPIe1lWbhQysyi8uttbtL9VVP2XTFuQ3E6oVHJr+0IR0LMVGJ+a8i6zsw==", "dependencies": { "tslib": "^2.3.0" }, @@ -524,14 +524,14 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/core": "14.2.5", + "@angular/core": "14.2.6", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.2.5.tgz", - "integrity": "sha512-L7d2/D6o9wlB2ugqRYpev6a8JntqS+7lF2o6z8y7RR2YAlAu71nq0BDsQez4/aSCK3HnDq0yhEnns7vcmOq/jA==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.2.6.tgz", + "integrity": "sha512-XtmJRNQQ/bUcRjB6jG67km3EPug8frnHH50sLqxye+cljCzWQpzFN/Qr1z0abuzEX8OC4alqxCDCFgTFyyVkaQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -539,7 +539,7 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/core": "14.2.5" + "@angular/core": "14.2.6" }, "peerDependenciesMeta": { "@angular/core": { @@ -548,9 +548,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.2.5.tgz", - "integrity": "sha512-3GYzTPw96TfJjw7Aso+f+uN6VFBWedqRATUQ6v+BAEyZIboirdLI1JQFOcCfuKWUM2B48RW+pdIduZmG3ckotA==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.2.6.tgz", + "integrity": "sha512-zKnpZ5WDbM31dwr5GDAbCblMIEUzWSglUyqCxJfbCg21dE0EuLfd/WzsROgM2TucOtCT5xNipqz4bc+wdEOIgQ==", "dev": true, "dependencies": { "@babel/core": "^7.17.2", @@ -573,14 +573,14 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/compiler": "14.2.5", + "@angular/compiler": "14.2.6", "typescript": ">=4.6.2 <4.9" } }, "node_modules/@angular/core": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.2.5.tgz", - "integrity": "sha512-Ok78Abq0puMGlolvNVzKFvsX7ePDkyxpZzztDzXDdRA4x4o6bAuuDG9Y7Wab2+wsdY6NktO+dFQjq1UBWClgSg==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.2.6.tgz", + "integrity": "sha512-fEIz7E488X03tLIqmWQRpahxRRU2SMjb9i/rMUjMQJkbppJC3cykl31bCYzeixNO+zpE55GPGuQX2qI/yDenZA==", "dependencies": { "tslib": "^2.3.0" }, @@ -593,9 +593,9 @@ } }, "node_modules/@angular/forms": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.2.5.tgz", - "integrity": "sha512-aMH5Vrftny0KF0XzWQIGfHoI0LVQ2aatpWzdUWiUqBeX/Q+ucmxeP5rZyKtUsi0flETWxdRZSBTjbXZ3dsIcTA==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.2.6.tgz", + "integrity": "sha512-t7Hd9RMnHbXRTdH/H8h8ZC3PsK1U4rH+XYaIbQNcys/XSf1uRFHx9MWqkwS5hoQEFOxkFSX5dRv2xSnHtxfq5w==", "dependencies": { "tslib": "^2.3.0" }, @@ -603,16 +603,16 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "14.2.5", - "@angular/core": "14.2.5", - "@angular/platform-browser": "14.2.5", + "@angular/common": "14.2.6", + "@angular/core": "14.2.6", + "@angular/platform-browser": "14.2.6", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/platform-browser": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.2.5.tgz", - "integrity": "sha512-FDZm23N9veSEouQX1YuZUjv7Nillroi+v0VbN1x5iPpFZEudaoZYT3A7bpJwdlxUx/4rGS0caaXNhN3CowtIeQ==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.2.6.tgz", + "integrity": "sha512-KQUN4YVYEK5NOL7QFnDulQta6tm9rPh/mruX/XCLkSmoRMlFBmsHyjx+VJBnBNUbUxNsBj7kknifOu9PqDgAWg==", "dependencies": { "tslib": "^2.3.0" }, @@ -620,9 +620,9 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/animations": "14.2.5", - "@angular/common": "14.2.5", - "@angular/core": "14.2.5" + "@angular/animations": "14.2.6", + "@angular/common": "14.2.6", + "@angular/core": "14.2.6" }, "peerDependenciesMeta": { "@angular/animations": { @@ -631,9 +631,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.2.5.tgz", - "integrity": "sha512-7W8oLs8YEGRr8izgUlpHgBfg3vUb5H0yicTHJY4zIqHJJbG1rTl46CjULaMjYM/FWcS8o7y6XJJcHx0c7pKNsw==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.2.6.tgz", + "integrity": "sha512-SlWEYLED4ST1AFfgeB8SyKLVJYp36XT+3Vw3yDrObsthzXCiFAuYHQZfSWgT1Sfx3uFqEdN7nskJqD05wN3mQg==", "dependencies": { "tslib": "^2.3.0" }, @@ -641,16 +641,16 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "14.2.5", - "@angular/compiler": "14.2.5", - "@angular/core": "14.2.5", - "@angular/platform-browser": "14.2.5" + "@angular/common": "14.2.6", + "@angular/compiler": "14.2.6", + "@angular/core": "14.2.6", + "@angular/platform-browser": "14.2.6" } }, "node_modules/@angular/router": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.2.5.tgz", - "integrity": "sha512-AUHcr9Lln7emJ/aete08UoqWQFZOLH1MhuP78r2pixvnNiZ9C8hcevX1rGGax0Po/Gy4PSJ4wnFhZPgifqCguQ==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.2.6.tgz", + "integrity": "sha512-Vz1kadGSqA7ZCZQ2woNbSBPMdiE5eTZv8cGympaFnFQQUzQTQ6zi22wY4RzovDk5Lw+EQkvOmaW2864LDzDeug==", "dependencies": { "tslib": "^2.3.0" }, @@ -658,9 +658,9 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "14.2.5", - "@angular/core": "14.2.5", - "@angular/platform-browser": "14.2.5", + "@angular/common": "14.2.6", + "@angular/core": "14.2.6", + "@angular/platform-browser": "14.2.6", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -2319,9 +2319,9 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.4.tgz", - "integrity": "sha512-5T2lY5vXqS+5UEit/5TwcIUeCnwgCljcF8IQRT6XRQPBrvLeq5V8W+URv+GvwoF3FP8tkhp++evVyDzkDGzNmA==", + "version": "7.19.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.5.tgz", + "integrity": "sha512-DxbNz9Lz4aMZ99qPpO1raTbcrI1ZeYh+9NR9qhfkQIbFtVEqotHojEBxHzmxhVONkGt6VyrqVQcgpefMy9pqcg==", "dev": true, "dependencies": { "@babel/types": "^7.19.4", @@ -2768,9 +2768,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz", - "integrity": "sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "3.1.0", @@ -2784,9 +2784,9 @@ "dev": true }, "node_modules/@microsoft/signalr": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-6.0.9.tgz", - "integrity": "sha512-DGVYe3ycT2PfRU7m3xCbv1HjhvClKl2VB1HyFlvf8SqBGXz3Cx+oalNWGYrGIgADA6Q2xaB4GaDmDdprTa2U0Q==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-6.0.10.tgz", + "integrity": "sha512-ND9LiIYac+ZDgCgW2QzpNfe9BTiOtjc2AX/2GtFIhRGhEzx5CixcNANg2VGj27IAxycAPPnEoy7+QA31Eil7QQ==", "dependencies": { "abort-controller": "^3.0.0", "eventsource": "^1.0.7", @@ -2795,30 +2795,10 @@ "ws": "^7.4.5" } }, - "node_modules/@microsoft/signalr/node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/@ngtools/webpack": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.5.tgz", - "integrity": "sha512-Thwq1WyOOq1PIWMcjAAqKI1hbvGC0ywxbNoDadOlWpEFm6k0dvXC6Zm9lnVkePjxlPfagvbnv55+Lv9Vmygc1g==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.6.tgz", + "integrity": "sha512-HdfoHLGPzyP135BOlvTQcpeWisVfiH0u40YNTBVK3QAsrLnY17e2QG5BWBOrVYipRu1975cZtTC9rPjcCY8aLQ==", "dev": true, "engines": { "node": "^14.15.0 || >=16.10.0", @@ -3220,9 +3200,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.8.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz", - "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==", + "version": "18.11.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.0.tgz", + "integrity": "sha512-IOXCvVRToe7e0ny7HpT/X9Rb2RYtElG1a+VshjwT00HxrM2dWBApHQoqsI6WiY7Q03vdf2bCrIGzVrkF/5t10w==", "dev": true }, "node_modules/@types/parse-json": { @@ -4170,9 +4150,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001418", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz", - "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==", + "version": "1.0.30001421", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001421.tgz", + "integrity": "sha512-Sw4eLbgUJAEhjLs1Fa+mk45sidp1wRn5y6GtDpHGBaNJ9OCDJaVh2tIaWWUnGfuXfKf1JCBaIarak3FkVAvEeA==", "dev": true, "funding": [ { @@ -4422,6 +4402,12 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4485,26 +4471,6 @@ "node": ">= 0.6" } }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -4515,13 +4481,10 @@ } }, "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true }, "node_modules/cookie": { "version": "0.4.2", @@ -4872,9 +4835,9 @@ } }, "node_modules/cssdb": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.0.1.tgz", - "integrity": "sha512-pT3nzyGM78poCKLAEy2zWIVX2hikq6dIrjuZzLV98MumBg+xMTNYfHx7paUlfiRTgg91O/vR889CIf+qiv79Rw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.0.2.tgz", + "integrity": "sha512-Vm4b6P/PifADu0a76H0DKRNVWq3Rq9xa/Nx6oEMUBJlwTUuZoZ3dkZxo8Gob3UEL53Cq+Ma1GBgISed6XEBs3w==", "dev": true, "funding": { "type": "opencollective", @@ -5132,9 +5095,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.276", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.276.tgz", - "integrity": "sha512-EpuHPqu8YhonqLBXHoU6hDJCD98FCe6KDoet3/gY1qsQ6usjJoHqBH2YIVs8FXaAtHwVL8Uqa/fsYao/vq9VWQ==", + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, "node_modules/emoji-regex": { @@ -5215,6 +5178,26 @@ "xmlhttprequest-ssl": "~2.0.0" } }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/engine.io-parser": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", @@ -5223,6 +5206,27 @@ "node": ">=10.0.0" } }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/enhanced-resolve": { "version": "5.10.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", @@ -5911,26 +5915,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/express/node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -6493,6 +6477,12 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/hpack.js/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -8205,10 +8195,13 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/minipass": { "version": "3.3.4", @@ -10602,10 +10595,24 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -11015,9 +11022,9 @@ } }, "node_modules/socket.io": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.2.tgz", - "integrity": "sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.3.tgz", + "integrity": "sha512-zdpnnKU+H6mOp7nYRXH4GNv1ux6HL6+lHL8g7Ds7Lj8CkdK1jJK/dlwsKDculbyOHifcJ0Pr/yeXnZQ5GeFrcg==", "dev": true, "dependencies": { "accepts": "~1.3.4", @@ -11290,26 +11297,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -11815,9 +11802,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", - "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", + "version": "0.7.32", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz", + "integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==", "dev": true, "funding": [ { @@ -12445,11 +12432,11 @@ "dev": true }, "node_modules/ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "engines": { - "node": ">=10.0.0" + "node": ">=8.3.0" }, "peerDependencies": { "bufferutil": "^4.0.1", @@ -12559,12 +12546,12 @@ } }, "@angular-devkit/architect": { - "version": "0.1402.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.5.tgz", - "integrity": "sha512-vtJEwB51UEY1Q7FCI7xGLdhdb2SRTtI1Qs0or95momn85NuxlaMQsXK1Wxu9/EwtWKZK8dXePXbB/hpiNt61JQ==", + "version": "0.1402.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.6.tgz", + "integrity": "sha512-qTmPBD7fBXBtlSapGLUEcJvRuL/O556zCFFpH3kSlzPNTYxi2falBjGY+4aG+078RXT1vVZtFsvRTart6VbhAg==", "dev": true, "requires": { - "@angular-devkit/core": "14.2.5", + "@angular-devkit/core": "14.2.6", "rxjs": "6.6.7" }, "dependencies": { @@ -12586,15 +12573,15 @@ } }, "@angular-devkit/build-angular": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.5.tgz", - "integrity": "sha512-jSgH11E+zs1C24lXj7R/PgXsTUpoYoMr1GtO6mpVROgXL5czVlL+b/B1p2HwbcAKuI9WXb48X6OZ6fOZhDQlSg==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.6.tgz", + "integrity": "sha512-XtaUwb3aZ8S0vl0y9bmbdFOH0KQCQ778twFH+ZfHW2BcPYtQz2Cy2rcVKXBQ850RyC0GxgMPfco6OGQndPpizg==", "dev": true, "requires": { "@ampproject/remapping": "2.2.0", - "@angular-devkit/architect": "0.1402.5", - "@angular-devkit/build-webpack": "0.1402.5", - "@angular-devkit/core": "14.2.5", + "@angular-devkit/architect": "0.1402.6", + "@angular-devkit/build-webpack": "0.1402.6", + "@angular-devkit/core": "14.2.6", "@babel/core": "7.18.10", "@babel/generator": "7.18.12", "@babel/helper-annotate-as-pure": "7.18.6", @@ -12605,7 +12592,7 @@ "@babel/runtime": "7.18.9", "@babel/template": "7.18.10", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "14.2.5", + "@ngtools/webpack": "14.2.6", "ansi-colors": "4.1.3", "babel-loader": "8.2.5", "babel-plugin-istanbul": "6.1.1", @@ -12676,12 +12663,12 @@ } }, "@angular-devkit/build-webpack": { - "version": "0.1402.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.5.tgz", - "integrity": "sha512-h+o0GZD9iATwWjaTiUR0lJ3QZ9twUOJ1sotRchXHzAXMuaDk8wqqPriL5S0qDMlA2QqpNt4OD9rodUCRwae7fw==", + "version": "0.1402.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.6.tgz", + "integrity": "sha512-gKsDxQ9pze0N1qDM0kdM4FfwpkjSOb0bQzqjZi7wTfrh/WGIQMCjG9CRwWT+Z289ZKaTpcQDPsDtOSo5QpKNDg==", "dev": true, "requires": { - "@angular-devkit/architect": "0.1402.5", + "@angular-devkit/architect": "0.1402.6", "rxjs": "6.6.7" }, "dependencies": { @@ -12703,9 +12690,9 @@ } }, "@angular-devkit/core": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.5.tgz", - "integrity": "sha512-lSje+HX0fx9Y2A4k63jVHrWdGT4wellhwcZpTCv9P6LvdfTkAlrfra3TaYhUPjavCsPwlRC/VVQN3Qkzk5m6gA==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.6.tgz", + "integrity": "sha512-qtRSdRm/h7C3ya04PJTDgQXV6mM8Y4RakANX1GTSXetCf9AVSxg74NJX76DWUgiHT4JiPYnJgJU6Hr/L0H6JOQ==", "dev": true, "requires": { "ajv": "8.11.0", @@ -12788,9 +12775,9 @@ } }, "@angular/animations": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.2.5.tgz", - "integrity": "sha512-4BhR9jSjgIwoK/alu7FSwSU5SxISMVFBAl/4cEYchfCqnflMNkZ8WwRVKTQjyeuYW5KtQTw9jRNp4tGK1YQWYw==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.2.6.tgz", + "integrity": "sha512-Tmb3Jj016j8m8OOGSk/ReL0b+OuUCMj0KQansk3C4pCpH9oPF67Vnm0fmVC2wYjjsSS/iDgl4kaDW740wfPGNQ==", "requires": { "tslib": "^2.3.0" } @@ -12882,25 +12869,25 @@ } }, "@angular/common": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.2.5.tgz", - "integrity": "sha512-v2fIK6imfMkUvYNjZQO+drE39QO3eSS95Yy7UN+6inb47DkAfzx6hipA9zKrMENjsS3kDv1d7cgDHE7WuOCzIw==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.2.6.tgz", + "integrity": "sha512-WNX7xe8LKP5DHPlae+c77PDwj0iIAAPIe1lWbhQysyi8uttbtL9VVP2XTFuQ3E6oVHJr+0IR0LMVGJ+a8i6zsw==", "requires": { "tslib": "^2.3.0" } }, "@angular/compiler": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.2.5.tgz", - "integrity": "sha512-L7d2/D6o9wlB2ugqRYpev6a8JntqS+7lF2o6z8y7RR2YAlAu71nq0BDsQez4/aSCK3HnDq0yhEnns7vcmOq/jA==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.2.6.tgz", + "integrity": "sha512-XtmJRNQQ/bUcRjB6jG67km3EPug8frnHH50sLqxye+cljCzWQpzFN/Qr1z0abuzEX8OC4alqxCDCFgTFyyVkaQ==", "requires": { "tslib": "^2.3.0" } }, "@angular/compiler-cli": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.2.5.tgz", - "integrity": "sha512-3GYzTPw96TfJjw7Aso+f+uN6VFBWedqRATUQ6v+BAEyZIboirdLI1JQFOcCfuKWUM2B48RW+pdIduZmG3ckotA==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.2.6.tgz", + "integrity": "sha512-zKnpZ5WDbM31dwr5GDAbCblMIEUzWSglUyqCxJfbCg21dE0EuLfd/WzsROgM2TucOtCT5xNipqz4bc+wdEOIgQ==", "dev": true, "requires": { "@babel/core": "^7.17.2", @@ -12916,41 +12903,41 @@ } }, "@angular/core": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.2.5.tgz", - "integrity": "sha512-Ok78Abq0puMGlolvNVzKFvsX7ePDkyxpZzztDzXDdRA4x4o6bAuuDG9Y7Wab2+wsdY6NktO+dFQjq1UBWClgSg==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.2.6.tgz", + "integrity": "sha512-fEIz7E488X03tLIqmWQRpahxRRU2SMjb9i/rMUjMQJkbppJC3cykl31bCYzeixNO+zpE55GPGuQX2qI/yDenZA==", "requires": { "tslib": "^2.3.0" } }, "@angular/forms": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.2.5.tgz", - "integrity": "sha512-aMH5Vrftny0KF0XzWQIGfHoI0LVQ2aatpWzdUWiUqBeX/Q+ucmxeP5rZyKtUsi0flETWxdRZSBTjbXZ3dsIcTA==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.2.6.tgz", + "integrity": "sha512-t7Hd9RMnHbXRTdH/H8h8ZC3PsK1U4rH+XYaIbQNcys/XSf1uRFHx9MWqkwS5hoQEFOxkFSX5dRv2xSnHtxfq5w==", "requires": { "tslib": "^2.3.0" } }, "@angular/platform-browser": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.2.5.tgz", - "integrity": "sha512-FDZm23N9veSEouQX1YuZUjv7Nillroi+v0VbN1x5iPpFZEudaoZYT3A7bpJwdlxUx/4rGS0caaXNhN3CowtIeQ==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.2.6.tgz", + "integrity": "sha512-KQUN4YVYEK5NOL7QFnDulQta6tm9rPh/mruX/XCLkSmoRMlFBmsHyjx+VJBnBNUbUxNsBj7kknifOu9PqDgAWg==", "requires": { "tslib": "^2.3.0" } }, "@angular/platform-browser-dynamic": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.2.5.tgz", - "integrity": "sha512-7W8oLs8YEGRr8izgUlpHgBfg3vUb5H0yicTHJY4zIqHJJbG1rTl46CjULaMjYM/FWcS8o7y6XJJcHx0c7pKNsw==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.2.6.tgz", + "integrity": "sha512-SlWEYLED4ST1AFfgeB8SyKLVJYp36XT+3Vw3yDrObsthzXCiFAuYHQZfSWgT1Sfx3uFqEdN7nskJqD05wN3mQg==", "requires": { "tslib": "^2.3.0" } }, "@angular/router": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.2.5.tgz", - "integrity": "sha512-AUHcr9Lln7emJ/aete08UoqWQFZOLH1MhuP78r2pixvnNiZ9C8hcevX1rGGax0Po/Gy4PSJ4wnFhZPgifqCguQ==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.2.6.tgz", + "integrity": "sha512-Vz1kadGSqA7ZCZQ2woNbSBPMdiE5eTZv8cGympaFnFQQUzQTQ6zi22wY4RzovDk5Lw+EQkvOmaW2864LDzDeug==", "requires": { "tslib": "^2.3.0" } @@ -14114,9 +14101,9 @@ }, "dependencies": { "@babel/generator": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.4.tgz", - "integrity": "sha512-5T2lY5vXqS+5UEit/5TwcIUeCnwgCljcF8IQRT6XRQPBrvLeq5V8W+URv+GvwoF3FP8tkhp++evVyDzkDGzNmA==", + "version": "7.19.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.5.tgz", + "integrity": "sha512-DxbNz9Lz4aMZ99qPpO1raTbcrI1ZeYh+9NR9qhfkQIbFtVEqotHojEBxHzmxhVONkGt6VyrqVQcgpefMy9pqcg==", "dev": true, "requires": { "@babel/types": "^7.19.4", @@ -14380,9 +14367,9 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz", - "integrity": "sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, "requires": { "@jridgewell/resolve-uri": "3.1.0", @@ -14396,29 +14383,21 @@ "dev": true }, "@microsoft/signalr": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-6.0.9.tgz", - "integrity": "sha512-DGVYe3ycT2PfRU7m3xCbv1HjhvClKl2VB1HyFlvf8SqBGXz3Cx+oalNWGYrGIgADA6Q2xaB4GaDmDdprTa2U0Q==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-6.0.10.tgz", + "integrity": "sha512-ND9LiIYac+ZDgCgW2QzpNfe9BTiOtjc2AX/2GtFIhRGhEzx5CixcNANg2VGj27IAxycAPPnEoy7+QA31Eil7QQ==", "requires": { "abort-controller": "^3.0.0", "eventsource": "^1.0.7", "fetch-cookie": "^0.11.0", "node-fetch": "^2.6.7", "ws": "^7.4.5" - }, - "dependencies": { - "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "requires": {} - } } }, "@ngtools/webpack": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.5.tgz", - "integrity": "sha512-Thwq1WyOOq1PIWMcjAAqKI1hbvGC0ywxbNoDadOlWpEFm6k0dvXC6Zm9lnVkePjxlPfagvbnv55+Lv9Vmygc1g==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.6.tgz", + "integrity": "sha512-HdfoHLGPzyP135BOlvTQcpeWisVfiH0u40YNTBVK3QAsrLnY17e2QG5BWBOrVYipRu1975cZtTC9rPjcCY8aLQ==", "dev": true, "requires": {} }, @@ -14740,9 +14719,9 @@ "dev": true }, "@types/node": { - "version": "18.8.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz", - "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==", + "version": "18.11.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.0.tgz", + "integrity": "sha512-IOXCvVRToe7e0ny7HpT/X9Rb2RYtElG1a+VshjwT00HxrM2dWBApHQoqsI6WiY7Q03vdf2bCrIGzVrkF/5t10w==", "dev": true }, "@types/parse-json": { @@ -15506,9 +15485,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001418", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz", - "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==", + "version": "1.0.30001421", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001421.tgz", + "integrity": "sha512-Sw4eLbgUJAEhjLs1Fa+mk45sidp1wRn5y6GtDpHGBaNJ9OCDJaVh2tIaWWUnGfuXfKf1JCBaIarak3FkVAvEeA==", "dev": true }, "chalk": { @@ -15694,6 +15673,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true } } }, @@ -15751,14 +15736,6 @@ "dev": true, "requires": { "safe-buffer": "5.2.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } } }, "content-type": { @@ -15768,13 +15745,10 @@ "dev": true }, "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true }, "cookie": { "version": "0.4.2", @@ -16020,9 +15994,9 @@ "dev": true }, "cssdb": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.0.1.tgz", - "integrity": "sha512-pT3nzyGM78poCKLAEy2zWIVX2hikq6dIrjuZzLV98MumBg+xMTNYfHx7paUlfiRTgg91O/vR889CIf+qiv79Rw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.0.2.tgz", + "integrity": "sha512-Vm4b6P/PifADu0a76H0DKRNVWq3Rq9xa/Nx6oEMUBJlwTUuZoZ3dkZxo8Gob3UEL53Cq+Ma1GBgISed6XEBs3w==", "dev": true }, "cssesc": { @@ -16207,9 +16181,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.276", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.276.tgz", - "integrity": "sha512-EpuHPqu8YhonqLBXHoU6hDJCD98FCe6KDoet3/gY1qsQ6usjJoHqBH2YIVs8FXaAtHwVL8Uqa/fsYao/vq9VWQ==", + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, "emoji-regex": { @@ -16266,6 +16240,15 @@ "debug": "~4.3.1", "engine.io-parser": "~5.0.3", "ws": "~8.2.3" + }, + "dependencies": { + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true, + "requires": {} + } } }, "engine.io-client": { @@ -16278,6 +16261,14 @@ "engine.io-parser": "~5.0.3", "ws": "~8.2.3", "xmlhttprequest-ssl": "~2.0.0" + }, + "dependencies": { + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "requires": {} + } } }, "engine.io-parser": { @@ -16715,12 +16706,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -17151,6 +17136,12 @@ "util-deprecate": "~1.0.1" } }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -18440,9 +18431,9 @@ } }, "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", "dev": true }, "minipass": { @@ -20125,9 +20116,9 @@ } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "safer-buffer": { @@ -20443,9 +20434,9 @@ "dev": true }, "socket.io": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.2.tgz", - "integrity": "sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.3.tgz", + "integrity": "sha512-zdpnnKU+H6mOp7nYRXH4GNv1ux6HL6+lHL8g7Ds7Lj8CkdK1jJK/dlwsKDculbyOHifcJ0Pr/yeXnZQ5GeFrcg==", "dev": true, "requires": { "accepts": "~1.3.4", @@ -20670,14 +20661,6 @@ "dev": true, "requires": { "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } } }, "string-width": { @@ -21039,9 +21022,9 @@ "dev": true }, "ua-parser-js": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", - "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", + "version": "0.7.32", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz", + "integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==", "dev": true }, "unicode-canonical-property-names-ecmascript": { @@ -21486,9 +21469,9 @@ "dev": true }, "ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "requires": {} }, "xmlhttprequest-ssl": { diff --git a/kdb-web/src/app/app.component.ts b/kdb-web/src/app/app.component.ts index 72139e66..1a2cf35f 100644 --- a/kdb-web/src/app/app.component.ts +++ b/kdb-web/src/app/app.component.ts @@ -1,4 +1,6 @@ import { Component, OnInit } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { PrimeNGConfig } from 'primeng/api'; import { AuthService } from './services/auth/auth.service'; import { SocketService } from './services/socket/socket.service'; import { ThemeService } from './services/theme/theme.service'; @@ -18,10 +20,10 @@ export class AppComponent implements OnInit { constructor( private authService: AuthService, private themeService: ThemeService, - private socket: SocketService - ) { } - - ngOnInit(): void { + private socket: SocketService, + private translateService: TranslateService, + private config: PrimeNGConfig + ) { this.themeService.sidebarWidth$.subscribe(value => { this.sidebarWidth = value; }); @@ -31,11 +33,33 @@ export class AppComponent implements OnInit { this.authService.isLoggedIn$.subscribe(value => { this.isLoggedIn = value; }); + } + + ngOnInit(): void { + this.translateService.setDefaultLang('en'); this.themeService.loadTheme(); this.socket.startSocket(); } + loadLang(): void { + let lang = localStorage.getItem(`default_lang`); + if (!lang) { + lang = 'en'; + this.setLang(lang); + } + this.translate(lang); + } + + setLang(lang: string): void { + localStorage.setItem(`default_lang`, lang); + } + + translate(lang: string) { + this.translateService.use(lang); + this.translateService.get('primeng').subscribe(res => this.config.setTranslation(res)); + } + setSideWidth($event: any): void { this.themeService.setSideWidth($event); diff --git a/kdb-web/src/app/components/header/header.component.ts b/kdb-web/src/app/components/header/header.component.ts index ff71d92f..441ceb58 100644 --- a/kdb-web/src/app/components/header/header.component.ts +++ b/kdb-web/src/app/components/header/header.component.ts @@ -29,7 +29,6 @@ export class HeaderComponent implements OnInit { ) { } ngOnInit(): void { - this.translateService.setDefaultLang('en'); this.initMenuLists(); this.loadLang(); this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { diff --git a/kdb-web/src/app/modules/auth/auth.module.ts b/kdb-web/src/app/modules/auth/auth.module.ts index 4e7c48c3..e0afca6a 100644 --- a/kdb-web/src/app/modules/auth/auth.module.ts +++ b/kdb-web/src/app/modules/auth/auth.module.ts @@ -1,18 +1,20 @@ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { SharedModule } from '../shared/shared.module'; import { AuthRoutingModule } from './auth-routing.module'; +import { AuthHeaderComponent } from './components/auth-header/auth-header.component'; import { ForgetPasswordComponent } from './components/forget-password/forget-password.component'; import { LoginComponent } from './components/login/login.component'; import { RegistrationComponent } from './components/registration/registration.component'; -import { SharedModule } from '../shared/shared.module'; @NgModule({ declarations: [ ForgetPasswordComponent, LoginComponent, - RegistrationComponent + RegistrationComponent, + AuthHeaderComponent ], imports: [ CommonModule, diff --git a/kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.html b/kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.html new file mode 100644 index 00000000..1b48c147 --- /dev/null +++ b/kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.html @@ -0,0 +1,10 @@ +
+
+ + +
+
+ + +
+
\ No newline at end of file diff --git a/kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.scss b/kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.spec.ts b/kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.spec.ts new file mode 100644 index 00000000..7afe71ab --- /dev/null +++ b/kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AuthHeaderComponent } from './auth-header.component'; + +describe('AuthHeaderComponent', () => { + let component: AuthHeaderComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AuthHeaderComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AuthHeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.ts b/kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.ts new file mode 100644 index 00000000..0858dc8a --- /dev/null +++ b/kdb-web/src/app/modules/auth/components/auth-header/auth-header.component.ts @@ -0,0 +1,83 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { TranslateService, LangChangeEvent } from '@ngx-translate/core'; +import { MenuItem, PrimeNGConfig } from 'primeng/api'; +import { catchError } from 'rxjs'; +import { AuthService } from 'src/app/services/auth/auth.service'; +import { SettingsService } from 'src/app/services/settings/settings.service'; +import { SpinnerService } from 'src/app/services/spinner/spinner.service'; +import { ThemeService } from 'src/app/services/theme/theme.service'; + +@Component({ + selector: 'app-auth-header', + templateUrl: './auth-header.component.html', + styleUrls: ['./auth-header.component.scss'] +}) +export class AuthHeaderComponent implements OnInit { + langList: MenuItem[] = []; + themeList: MenuItem[] = []; + userMenuList!: MenuItem[]; + + constructor( + private router: Router, + private themeService: ThemeService, + private spinnerService: SpinnerService, + private settings: SettingsService, + private translateService: TranslateService, + private config: PrimeNGConfig + ) { } + + ngOnInit(): void { + this.initMenuLists(); + this.loadLang(); + } + + initMenuLists(): void { + this.langList = [ + { + label: 'English', command: () => { + this.translate('en'); + this.setLang('en'); + }, + }, + { + label: 'Deutsch', command: () => { + this.translate('de'); + this.setLang('de'); + }, + }, + ]; + + this.settings.getThemes()?.forEach(theme => { + this.themeList.push({ + label: theme.Label, + command: () => { + this.changeTheme(theme.Name); + } + }); + }); + } + + changeTheme(name: string): void { + this.themeService.setTheme(name, true); + } + + translate(lang: string) { + this.translateService.use(lang); + this.translateService.get('primeng').subscribe(res => this.config.setTranslation(res)); + } + + loadLang(): void { + let lang = localStorage.getItem(`default_lang`); + if (!lang) { + lang = 'en'; + this.setLang(lang); + } + this.translate(lang); + } + + setLang(lang: string): void { + localStorage.setItem(`default_lang`, lang); + } + +} diff --git a/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.html b/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.html index e1fee63e..71f7761e 100644 --- a/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.html +++ b/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.html @@ -5,24 +5,26 @@

{{'auth.header' | translate}}

- +
{{'auth.forgot_password.send_confirmation_url' | translate}}
@@ -33,8 +35,8 @@

{{'auth.header' | translate}}

- +
@@ -42,16 +44,20 @@ placeholder="{{'auth.forgot_password.repeat_password' | translate}}" [ngClass]="{ 'invalid-feedback-input': submitted && repeatErrors.password}">
-
{{'auth.forgot_password.passwords_do_not_match' | translate}}
+
{{'auth.forgot_password.passwords_do_not_match' | + translate}}
+ +
\ No newline at end of file diff --git a/kdb-web/src/app/modules/auth/components/login/login.component.html b/kdb-web/src/app/modules/auth/components/login/login.component.html index 43ea33cd..54789bee 100644 --- a/kdb-web/src/app/modules/auth/components/login/login.component.html +++ b/kdb-web/src/app/modules/auth/components/login/login.component.html @@ -4,7 +4,7 @@

sh-edraft.de

-
- E-Mail wird benötigt
-
Benutzer nicht gefunden
-
E-Mail wurde nicht bestätigt
+ {{'auth.login.e_mail_required' | translate}}
+
{{'auth.login.user_not_found' | translate}}
+
{{'auth.login.e_mail_not_confirmed' | translate}}
@@ -24,7 +24,7 @@ styleClass="p-password p-component p-inputwrapper p-input-icon-right" Remove after update! --> -
- Password wird benötigt
-
Falsches passwort
+ {{'auth.login.password_required' | translate}}
+
{{'auth.login.wrong_password' | translate}}
+ + \ No newline at end of file diff --git a/kdb-web/src/app/modules/auth/components/registration/registration.component.html b/kdb-web/src/app/modules/auth/components/registration/registration.component.html index 2d9f41bd..ba7eac00 100644 --- a/kdb-web/src/app/modules/auth/components/registration/registration.component.html +++ b/kdb-web/src/app/modules/auth/components/registration/registration.component.html @@ -4,80 +4,82 @@

sh-edraft.de

-
- Vorname wird benötigt
-
Vorname ist ungültig
+ {{'auth.register.first_name_required' | translate}}
+
{{'auth.register.first_name_invalid' | translate}}
-
- Nachname wird benötigt
-
Nachname ist ungültig
+ {{'auth.register.last_name_required' | translate}}
+
{{'auth.register.last_name_invalid' | translate}}
-
- E-Mail wird benötigt
-
Benutzer existiert bereits
+ {{'auth.register.e_mail_required' | translate}}
+
{{'auth.register.user_already_exists' | translate}}
-
-
Die E-Mails stimmen nicht überein
+
{{'auth.register.e_mails_not_match' | translate}}
-
- Password wird benötigt
+ {{'auth.register.password_required' | translate}}
-
-
Die Passwörter stimmen nicht überein
+
{{'auth.register.passwords_not_match' | translate}}
+ + \ No newline at end of file diff --git a/kdb-web/src/app/services/theme/theme.service.ts b/kdb-web/src/app/services/theme/theme.service.ts index 6dc3410a..6abe0955 100644 --- a/kdb-web/src/app/services/theme/theme.service.ts +++ b/kdb-web/src/app/services/theme/theme.service.ts @@ -41,7 +41,7 @@ export class ThemeService { const mail = this.authService.getEMailFromDecodedToken(token); let defaultThemeName = localStorage.getItem(`default_themeName`); - if (!defaultThemeName || defaultThemeName != Themes.Default) { + if (!defaultThemeName) { defaultThemeName = Themes.Default; } @@ -58,7 +58,13 @@ export class ThemeService { this.setTheme(userThemeName); } - setTheme(name: string): void { + setTheme(name: string, isDefault: boolean = false): void { + if (isDefault) { + localStorage.setItem(`default_themeName`, name); + this.themeName$.next(name); + return; + } + this.authService.isUserLoggedInAsync().then(result => { if (!result) { localStorage.setItem(`default_themeName`, Themes.Default); diff --git a/kdb-web/src/assets/i18n/de.json b/kdb-web/src/assets/i18n/de.json index 9eec0bf3..9f305603 100644 --- a/kdb-web/src/assets/i18n/de.json +++ b/kdb-web/src/assets/i18n/de.json @@ -89,8 +89,37 @@ }, "auth": { "header": "Krümmelmonster WI", - "login": {}, - "register": {}, + "login": { + "e_mail": "E-Mail", + "password": "Passwort", + "login": "Einloggen", + "register": "Registrieren", + "forgot_password": "Passwort vergessen?", + "e_mail_required": "E-Mail benötigt", + "user_not_found": "Benutzer nicht gefunden", + "e_mail_not_confirmed": "E-Mail nicht bestätigt", + "password_required": "Passwort benötigt", + "wrong_password": "Falsches passwort" + }, + "register": { + "first_name": "Vorname", + "last_name": "Nachname", + "e_mail": "E-Mail", + "repeat_e_mail": "E-mail wiederholen", + "password": "Password", + "repeat_password": "password wiederholen", + "register": "Registrieren", + "login": "Einloggen", + "user_already_exists": "Benutzer existiert bereits", + "passwords_not_match": "Passwörter sitmmen nicht überein", + "e_mails_not_match": "E-Mails sitmmen nicht überein", + "first_name_required": "Vorname benötigt", + "last_name_required": "Nachname benötigt", + "e_mail_required": "E-Mail benötigt", + "password_required": "Passwort benötigt", + "first_name_invalid": "Vorname ungültig", + "last_name_invalid": "Nachname ungültig" + }, "forgot_password": { "e_mail": "E-Mail", "send_confirmation_url": "Falls ein Benutzer mit der E-Mail gefunden wurde, wurde Betstätigungslink versendet", diff --git a/kdb-web/src/assets/i18n/en.json b/kdb-web/src/assets/i18n/en.json index d289c560..0851c3e4 100644 --- a/kdb-web/src/assets/i18n/en.json +++ b/kdb-web/src/assets/i18n/en.json @@ -92,7 +92,7 @@ "password": "Password", "login": "Login", "register": "Register", - "forgot_password": "Forgot password", + "forgot_password": "Forgot password?", "e_mail_required": "E-Mail required", "user_not_found": "User not found", "e_mail_not_confirmed": "Email was not confirmed", @@ -114,7 +114,7 @@ "first_name_required": "Forename required", "last_name_required": "Surname required", "e_mail_required": "E-Mail required", - "password_required": "Passwort required", + "password_required": "Password required", "first_name_invalid": "Forename invalid", "last_name_invalid": "Surname invalid" }, diff --git a/kdb-web/src/styles.scss b/kdb-web/src/styles.scss index 8bbd6cd8..58c34115 100644 --- a/kdb-web/src/styles.scss +++ b/kdb-web/src/styles.scss @@ -68,6 +68,12 @@ header { } } +.auth-header { + .p-menu-overlay { + width: 180px !important; + } +} + .app { height: 100%; display: flex; @@ -361,8 +367,11 @@ footer { display: flex; justify-content: center; align-items: center; + flex-direction: column; + gap: 15px; - .login-form-wrapper { + .login-form-wrapper, + .auth-header { width: 350px; height: 350px; @@ -418,6 +427,11 @@ footer { .register-confirm-form-wrapper { height: 250px; } + + .auth-header { + width: 350px; + height: 75px; + } } .input-field { diff --git a/kdb-web/src/styles/primeng-fixes.scss b/kdb-web/src/styles/primeng-fixes.scss index 5bad03ee..c6b246f4 100644 --- a/kdb-web/src/styles/primeng-fixes.scss +++ b/kdb-web/src/styles/primeng-fixes.scss @@ -23,8 +23,11 @@ } } -.p-menu-overlay { - top: $headerHeight !important; +header, +.app { + .p-menu-overlay { + top: $headerHeight !important; + } } .p-panelmenu { diff --git a/kdb-web/src/styles/themes/default-dark-theme.scss b/kdb-web/src/styles/themes/default-dark-theme.scss index 8a5b60fa..9f0e2f6e 100644 --- a/kdb-web/src/styles/themes/default-dark-theme.scss +++ b/kdb-web/src/styles/themes/default-dark-theme.scss @@ -194,7 +194,8 @@ .login-wrapper { background-color: $secondaryBackgroundColor; - .login-form-wrapper { + .login-form-wrapper, + .auth-header { background-color: $primaryBackgroundColor; .login-form { @@ -424,7 +425,7 @@ } input, - .p-password { + .p-password input { border-radius: 10px; border: $default-border; diff --git a/kdb-web/src/styles/themes/default-light-theme.scss b/kdb-web/src/styles/themes/default-light-theme.scss index bc58a6f7..69a1b97d 100644 --- a/kdb-web/src/styles/themes/default-light-theme.scss +++ b/kdb-web/src/styles/themes/default-light-theme.scss @@ -194,7 +194,8 @@ .login-wrapper { background-color: $secondaryBackgroundColor; - .login-form-wrapper { + .login-form-wrapper, + .auth-header { background-color: $primaryBackgroundColor; .login-form { @@ -424,7 +425,7 @@ } input, - .p-password { + .p-password input { border-radius: 10px; border: $default-border; diff --git a/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss b/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss index 9e45019f..cef76707 100644 --- a/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss +++ b/kdb-web/src/styles/themes/sh-edraft-dark-theme.scss @@ -196,7 +196,8 @@ .login-wrapper { background-color: $secondaryBackgroundColor; - .login-form-wrapper { + .login-form-wrapper, + .auth-header { background-color: $primaryBackgroundColor; .login-form { @@ -426,7 +427,7 @@ } input, - .p-password { + .p-password input { border-radius: 10px; border: $default-border; diff --git a/kdb-web/src/styles/themes/sh-edraft-light-theme.scss b/kdb-web/src/styles/themes/sh-edraft-light-theme.scss index 26c70cd5..8ae5f724 100644 --- a/kdb-web/src/styles/themes/sh-edraft-light-theme.scss +++ b/kdb-web/src/styles/themes/sh-edraft-light-theme.scss @@ -194,7 +194,8 @@ .login-wrapper { background-color: $secondaryBackgroundColor; - .login-form-wrapper { + .login-form-wrapper, + .auth-header { background-color: $primaryBackgroundColor; .login-form { @@ -424,7 +425,7 @@ } input, - .p-password { + .p-password input { border-radius: 10px; border: $default-border; From a082b879ca08190715b379ee07962e9ccfedaba2 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 18 Oct 2022 18:33:03 +0200 Subject: [PATCH 049/275] Secured password handling #70 --- .../src/bot_api/controller/auth_controller.py | 2 +- kdb-bot/src/bot_api/service/auth_service.py | 19 +++++++++++-------- .../transformer/auth_user_transformer.py | 3 ++- .../src/bot_data/migration/api_migration.py | 3 ++- kdb-bot/src/bot_data/model/auth_user.py | 18 ++++++++++++++++-- .../service/auth_user_repository_service.py | 5 +++-- 6 files changed, 35 insertions(+), 15 deletions(-) diff --git a/kdb-bot/src/bot_api/controller/auth_controller.py b/kdb-bot/src/bot_api/controller/auth_controller.py index 78a17e79..b74c9518 100644 --- a/kdb-bot/src/bot_api/controller/auth_controller.py +++ b/kdb-bot/src/bot_api/controller/auth_controller.py @@ -56,7 +56,7 @@ class AuthController: return jsonify(result.to_dict()) @Route.get(f'{BasePath}/users/get/') - @Route.authorize(role=AuthRoleEnum.admin) + @Route.authorize async def get_user_from_email(self, email: str) -> Response: result = await self._auth_service.get_auth_user_by_email_async(email) return jsonify(result.to_dict()) diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index b2056530..5887bb9a 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -63,8 +63,8 @@ class AuthService(AuthServiceABC): return mail @staticmethod - def _hash_sha256(password: str) -> str: - return hashlib.sha256(password.encode('utf-8')).hexdigest() + def _hash_sha256(password: str, salt: str) -> str: + return hashlib.sha256(f'{password}{salt}'.encode('utf-8')).hexdigest() @staticmethod def _is_email_valid(email: str) -> bool: @@ -188,8 +188,9 @@ class AuthService(AuthServiceABC): if db_user is not None: raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') - user_dto.password = self._hash_sha256(user_dto.password) user = AUT.to_db(user_dto) + user.password_salt = uuid.uuid4() + user.password = self._hash_sha256(user_dto.password, user.password_salt) if not self._is_email_valid(user.email): raise ServiceException(ServiceErrorCode.InvalidData, 'Invalid E-Mail address') @@ -244,17 +245,18 @@ class AuthService(AuthServiceABC): # hash passwords in DTOs if update_user_dto.auth_user.password is not None and update_user_dto.auth_user.password != '': is_existing_password_set = True - update_user_dto.auth_user.password = self._hash_sha256(update_user_dto.auth_user.password) + update_user_dto.auth_user.password = self._hash_sha256(update_user_dto.auth_user.password, user.password_salt) if update_user_dto.auth_user.password != user.password: raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password') if update_user_dto.new_auth_user.password is not None and update_user_dto.new_auth_user.password != '': is_new_password_set = True - update_user_dto.new_auth_user.password = self._hash_sha256(update_user_dto.new_auth_user.password) + update_user_dto.new_auth_user.password = self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) # update password if is_existing_password_set and is_new_password_set and update_user_dto.auth_user.password != update_user_dto.new_auth_user.password: + user.password_salt = uuid.uuid4() user.password = update_user_dto.new_auth_user.password self._auth_users.update_auth_user(user) @@ -301,7 +303,8 @@ class AuthService(AuthServiceABC): # update password if update_user_dto.change_password and update_user_dto.auth_user.password != update_user_dto.new_auth_user.password: - user.password = self._hash_sha256(update_user_dto.new_auth_user.password) + user.password_salt = uuid.uuid4() + user.password = self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) # update role if user.auth_role == update_user_dto.auth_user.auth_role and user.auth_role != update_user_dto.new_auth_user.auth_role: @@ -350,7 +353,7 @@ class AuthService(AuthServiceABC): if db_user is None: raise ServiceException(ServiceErrorCode.InvalidUser, f'User not found') - user_dto.password = self._hash_sha256(user_dto.password) + user_dto.password = self._hash_sha256(user_dto.password, db_user.password_salt) if db_user.password != user_dto.password: raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password') @@ -432,5 +435,5 @@ class AuthService(AuthServiceABC): if user.password is None or rp_dto.password == '': raise ServiceException(ServiceErrorCode.InvalidData, f'Password not set') - user.password = self._hash_sha256(rp_dto.password) + user.password = self._hash_sha256(rp_dto.password, user.password_salt) self._db.save_changes() diff --git a/kdb-bot/src/bot_api/transformer/auth_user_transformer.py b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py index b5608e23..768cc7ef 100644 --- a/kdb-bot/src/bot_api/transformer/auth_user_transformer.py +++ b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py @@ -18,6 +18,7 @@ class AuthUserTransformer(TransformerABC): None, None, None, + None, datetime.now(tz=timezone.utc), AuthRoleEnum.normal if dto.auth_role is None else AuthRoleEnum(dto.auth_role), dto.user_id, @@ -31,7 +32,7 @@ class AuthUserTransformer(TransformerABC): db.first_name, db.last_name, db.email, - db.password, + '', db.confirmation_id, db.auth_role, db.user_id diff --git a/kdb-bot/src/bot_data/migration/api_migration.py b/kdb-bot/src/bot_data/migration/api_migration.py index 6860ae14..8466ec46 100644 --- a/kdb-bot/src/bot_data/migration/api_migration.py +++ b/kdb-bot/src/bot_data/migration/api_migration.py @@ -23,12 +23,13 @@ class ApiMigration(MigrationABC): `LastName` VARCHAR(255), `EMail` VARCHAR(255), `Password` VARCHAR(255), + `PasswordSalt` VARCHAR(255), `RefreshToken` VARCHAR(255), `ConfirmationId` VARCHAR(255) DEFAULT NULL, `ForgotPasswordId` VARCHAR(255) DEFAULT NULL, `RefreshTokenExpiryTime` DATETIME(6) NOT NULL, `AuthRole` INT NOT NULL DEFAULT '0', - `UserId` BIGINT NOT NULL DEFAULT '0', + `UserId` BIGINT DEFAULT NULL, `CreatedOn` DATETIME(6) NOT NULL, `LastModifiedOn` DATETIME(6) NOT NULL, PRIMARY KEY(`Id`), diff --git a/kdb-bot/src/bot_data/model/auth_user.py b/kdb-bot/src/bot_data/model/auth_user.py index fe92ad1e..58c9f982 100644 --- a/kdb-bot/src/bot_data/model/auth_user.py +++ b/kdb-bot/src/bot_data/model/auth_user.py @@ -1,3 +1,4 @@ +import uuid from datetime import datetime from typing import Optional from cpl_core.database import TableABC @@ -14,6 +15,7 @@ class AuthUser(TableABC): last_name: str, email: str, password: str, + password_salt: Optional[str], refresh_token: Optional[str], confirmation_id: Optional[str], forgot_password_id: Optional[str], @@ -29,6 +31,7 @@ class AuthUser(TableABC): self._last_name = last_name self._email = email self._password = password + self._password_salt = uuid.uuid4() if password_salt is None else password_salt self._refresh_token = refresh_token self._confirmation_id = confirmation_id self._forgot_password_id = forgot_password_id @@ -77,6 +80,14 @@ class AuthUser(TableABC): def password(self, value: str): self._password = value + @property + def password_salt(self) -> str: + return self._password_salt + + @password_salt.setter + def password_salt(self, value: str): + self._password_salt = value + @property def refresh_token(self) -> Optional[str]: return self._refresh_token @@ -168,6 +179,7 @@ class AuthUser(TableABC): `LastName`, `EMail`, `Password`, + `PasswordSalt`, `RefreshToken`, `ConfirmationId`, `ForgotPasswordId`, @@ -182,12 +194,13 @@ class AuthUser(TableABC): '{self._last_name}', '{self._email}', '{self._password}', - '{self._refresh_token}', + '{self._password_salt}', + '{"NULL" if self._refresh_token is None else self._refresh_token}', '{"NULL" if self._confirmation_id is None else self._confirmation_id}', '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', '{self._refresh_token_expire_time}', {self._auth_role_id.value}, - {"NULL" if self._user_id is None else self._user_id} + {"NULL" if self._user_id is None else self._user_id}, '{self._created_at}', '{self._modified_at}' ) @@ -201,6 +214,7 @@ class AuthUser(TableABC): `LastName` = '{self._last_name}', `EMail` = '{self._email}', `Password` = '{self._password}', + `PasswordSalt` = '{self._password_salt}', `RefreshToken` = '{self._refresh_token}', `ConfirmationId` = '{"NULL" if self._confirmation_id is None else self._confirmation_id}', `ForgotPasswordId` = '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', diff --git a/kdb-bot/src/bot_data/service/auth_user_repository_service.py b/kdb-bot/src/bot_data/service/auth_user_repository_service.py index c8fee7b4..ce8e3e4b 100644 --- a/kdb-bot/src/bot_data/service/auth_user_repository_service.py +++ b/kdb-bot/src/bot_data/service/auth_user_repository_service.py @@ -36,8 +36,9 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): self._get_value_from_result(result[6]), self._get_value_from_result(result[7]), self._get_value_from_result(result[8]), - AuthRoleEnum(self._get_value_from_result(result[9])), - self._get_value_from_result(result[10]), + self._get_value_from_result(result[9]), + AuthRoleEnum(self._get_value_from_result(result[10])), + self._get_value_from_result(result[11]), id=self._get_value_from_result(result[0]) ) From 47a73a42981fe198bdce951d8af8ed039f0d2e08 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 18 Oct 2022 19:50:13 +0200 Subject: [PATCH 050/275] Fixed password handling #70 --- .../src/bot_api/controller/auth_controller.py | 11 +++++++-- .../src/bot_api/model/update_auth_user_dto.py | 4 ++-- kdb-bot/src/bot_api/service/auth_service.py | 23 +++++++------------ 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/kdb-bot/src/bot_api/controller/auth_controller.py b/kdb-bot/src/bot_api/controller/auth_controller.py index b74c9518..2d03b37a 100644 --- a/kdb-bot/src/bot_api/controller/auth_controller.py +++ b/kdb-bot/src/bot_api/controller/auth_controller.py @@ -12,6 +12,7 @@ from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria 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.reset_password_dto import ResetPasswordDTO from bot_api.model.token_dto import TokenDTO from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO from bot_api.route.route import Route @@ -62,7 +63,7 @@ class AuthController: return jsonify(result.to_dict()) @Route.get(f'{BasePath}/users/find/') - @Route.authorize(role=AuthRoleEnum.admin) + @Route.authorize async def find_user_from_email(self, email: str) -> Response: result = await self._auth_service.find_auth_user_by_email_async(email) return jsonify(result.to_dict()) @@ -99,7 +100,13 @@ class AuthController: @Route.post(f'{BasePath}/confirm-forgot-password/') async def confirm_forgot_password(self, id: str): - await self._auth_service.confirm_forgot_password_async(id) + result = await self._auth_service.confirm_forgot_password_async(id) + return jsonify(result.to_dict()) + + @Route.post(f'{BasePath}/reset-password') + async def reset_password(self): + dto: ResetPasswordDTO = JSONProcessor.process(ResetPasswordDTO, request.get_json(force=True, silent=True)) + await self._auth_service.reset_password_async(dto) return '', 200 @Route.post(f'{BasePath}/update-user') diff --git a/kdb-bot/src/bot_api/model/update_auth_user_dto.py b/kdb-bot/src/bot_api/model/update_auth_user_dto.py index 9caa9473..254c0c72 100644 --- a/kdb-bot/src/bot_api/model/update_auth_user_dto.py +++ b/kdb-bot/src/bot_api/model/update_auth_user_dto.py @@ -12,7 +12,7 @@ class UpdateAuthUserDTO(DtoABC): self, auth_user_dto: AuthUserDTO, new_auth_user_dto: AuthUserDTO, - change_password=False + change_password: bool = False ): DtoABC.__init__(self) @@ -35,7 +35,7 @@ class UpdateAuthUserDTO(DtoABC): def from_dict(self, values: dict): self._auth_user = values['authUser'] self._new_auth_user = values['newAuthUser'] - self._change_password = False if 'changePassword' not in values else values['changePassword'] + self._change_password = False if 'changePassword' not in values else bool(values['changePassword']) def to_dict(self) -> dict: return { diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index 5887bb9a..3057cd84 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -240,24 +240,14 @@ class AuthService(AuthServiceABC): raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') user.email = update_user_dto.new_auth_user.email - is_existing_password_set = False - is_new_password_set = False - # hash passwords in DTOs - if update_user_dto.auth_user.password is not None and update_user_dto.auth_user.password != '': - is_existing_password_set = True - update_user_dto.auth_user.password = self._hash_sha256(update_user_dto.auth_user.password, user.password_salt) - + update_user_dto.auth_user.password = self._hash_sha256(update_user_dto.auth_user.password, user.password_salt) if update_user_dto.auth_user.password != user.password: raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password') - if update_user_dto.new_auth_user.password is not None and update_user_dto.new_auth_user.password != '': - is_new_password_set = True - update_user_dto.new_auth_user.password = self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) - # update password - if is_existing_password_set and is_new_password_set and update_user_dto.auth_user.password != update_user_dto.new_auth_user.password: + if self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) != user.password: user.password_salt = uuid.uuid4() - user.password = update_user_dto.new_auth_user.password + user.password = self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) self._auth_users.update_auth_user(user) self._db.save_changes() @@ -302,7 +292,7 @@ class AuthService(AuthServiceABC): user.email = update_user_dto.new_auth_user.email # update password - if update_user_dto.change_password and update_user_dto.auth_user.password != update_user_dto.new_auth_user.password: + if update_user_dto.change_password and user.password != self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt): user.password_salt = uuid.uuid4() user.password = self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) @@ -340,7 +330,7 @@ class AuthService(AuthServiceABC): if user is None: raise ServiceException(ServiceErrorCode.InvalidData, 'Token expired') except Exception as e: - self._logger.error(__name__, f'Refreshing token failed', e) + self._logger.error(__name__, f'Token invalid', e) return False return True @@ -435,5 +425,8 @@ class AuthService(AuthServiceABC): if user.password is None or rp_dto.password == '': raise ServiceException(ServiceErrorCode.InvalidData, f'Password not set') + user.password_salt = uuid.uuid4() user.password = self._hash_sha256(rp_dto.password, user.password_salt) + user.forgot_password_id = None + self._auth_users.update_auth_user(user) self._db.save_changes() From d0ded956cb3fa5c75eb156a7699470ed1e00e175 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 18 Oct 2022 21:00:13 +0200 Subject: [PATCH 051/275] [WIP] Fixed forgot password #70 --- kdb-bot/src/bot/bot.json | 4 +-- kdb-bot/src/bot_api/service/auth_service.py | 27 ++++++++++--------- .../forget-password.component.ts | 1 + .../components/login/login.component.html | 2 +- kdb-web/src/app/services/auth/auth.service.ts | 6 ++--- kdb-web/src/styles.scss | 10 +++---- 6 files changed, 26 insertions(+), 24 deletions(-) diff --git a/kdb-bot/src/bot/bot.json b/kdb-bot/src/bot/bot.json index 985c978b..f7fdb2cf 100644 --- a/kdb-bot/src/bot/bot.json +++ b/kdb-bot/src/bot/bot.json @@ -16,10 +16,10 @@ "LicenseName": "MIT", "LicenseDescription": "MIT, see LICENSE for more details.", "Dependencies": [ - "cpl-core==2022.10.0.post6", + "cpl-core==2022.10.0.post7", "cpl-translation==2022.10.0.post1", "cpl-query==2022.10.0.post2", - "cpl-discord==2022.10.0.post5", + "cpl-discord==2022.10.0.post6", "Flask==2.2.2", "Flask-Classful==0.14.2", "Flask-Cors==3.0.10", diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index 3057cd84..15fa8138 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -6,6 +6,7 @@ from typing import Optional import jwt from cpl_core.database.context import DatabaseContextABC +from cpl_core.environment import ApplicationEnvironmentABC from cpl_core.mailing import EMailClientABC, EMail from cpl_query.extension import List from cpl_translation import TranslatePipe @@ -35,6 +36,7 @@ class AuthService(AuthServiceABC): def __init__( self, + env: ApplicationEnvironmentABC, logger: ApiLogger, auth_users: AuthUserRepositoryABC, db: DatabaseContextABC, @@ -46,6 +48,7 @@ class AuthService(AuthServiceABC): ): AuthServiceABC.__init__(self) + self._environment = env self._logger = logger self._auth_users = auth_users self._db = db @@ -54,14 +57,6 @@ class AuthService(AuthServiceABC): self._auth_settings = auth_settings self._frontend_settings = frontend_settings - @staticmethod - def _get_mail_to_send() -> EMail: - mail = EMail() - mail.add_header('Mime-Version: 1.0') - mail.add_header('Content-Type: text/plain charset=utf-8') - mail.add_header('Content-Transfer-Encoding: quoted-printable') - return mail - @staticmethod def _hash_sha256(password: str, salt: str) -> str: return hashlib.sha256(f'{password}{salt}'.encode('utf-8')).hexdigest() @@ -141,10 +136,14 @@ class AuthService(AuthServiceABC): if not url.endswith('/'): url = f'{url}/' - mail = self._get_mail_to_send() + mail = EMail() + mail.add_header('Mime-Version: 1.0') + mail.add_header('Content-Type: text/plain charset=utf-8') + mail.add_header('Content-Transfer-Encoding: quoted-printable') mail.add_receiver(user.email) mail.subject = self._t.transform('api.auth.confirmation.subject').format(user.first_name, user.last_name) mail.body = self._t.transform('api.auth.confirmation.message').format(url, user.confirmation_id) + mail.body += f'\n\nDies ist eine automatische E-Mail.\nGesendet von {self._environment.application_name}-{self._environment.environment_name}@{self._environment.host_name}' self._mailer.send_mail(mail) def _send_forgot_password_id_to_user(self, user: AuthUser): @@ -152,10 +151,14 @@ class AuthService(AuthServiceABC): if not url.endswith('/'): url = f'{url}/' - mail = self._get_mail_to_send() + mail = EMail() + mail.add_header('Mime-Version: 1.0') + mail.add_header('Content-Type: text/plain charset=utf-8') + mail.add_header('Content-Transfer-Encoding: quoted-printable') mail.add_receiver(user.email) - mail.subject = self._t.transform('api.auth.forgot_password.subject').format(user.first_name, user.last_name) - mail.body = self._t.transform('api.auth.forgot_password.message').format(url, user.forgot_password_id) + mail.subject = str(self._t.transform('api.auth.forgot_password.subject').format(user.first_name, user.last_name)) + mail.body = str(self._t.transform('api.auth.forgot_password.message').format(url, user.forgot_password_id)) + mail.body += f'\n\nDies ist eine automatische E-Mail.\nGesendet von {self._environment.application_name}-{self._environment.environment_name}@{self._environment.host_name}' self._mailer.send_mail(mail) async def get_all_auth_users_async(self) -> List[AuthUserDTO]: diff --git a/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.ts b/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.ts index c49c6be1..f73bd20e 100644 --- a/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.ts +++ b/kdb-web/src/app/modules/auth/components/forget-password/forget-password.component.ts @@ -37,6 +37,7 @@ export class ForgetPasswordComponent implements OnInit { ) { } ngOnInit(): void { + console.log('test'); this.spinnerService.showSpinner(); this.authService.isUserLoggedInAsync().then(result => { if (result) { diff --git a/kdb-web/src/app/modules/auth/components/login/login.component.html b/kdb-web/src/app/modules/auth/components/login/login.component.html index 54789bee..d6ee0326 100644 --- a/kdb-web/src/app/modules/auth/components/login/login.component.html +++ b/kdb-web/src/app/modules/auth/components/login/login.component.html @@ -2,7 +2,7 @@ - \ No newline at end of file + From 1f2087f50f9720beeaabe381b6fd25ba33aaf54a Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 25 Oct 2022 21:11:55 +0200 Subject: [PATCH 097/275] Fixed update user #85 --- kdb-bot/src/bot_api/service/auth_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index 4c9fcaf1..bf5d9f86 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -349,7 +349,7 @@ class AuthService(AuthServiceABC): raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password') # update password - if self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) != user.password: + if update_user_dto.new_auth_user.password is not None and self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) != user.password: user.password_salt = uuid.uuid4() user.password = self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) From ac5271de6d7b7a45bbc3d82d2c465ae2ee8dbb10 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 25 Oct 2022 21:15:40 +0200 Subject: [PATCH 098/275] Fixed update user #85 --- kdb-bot/src/bot_api/service/auth_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index bf5d9f86..ba45a155 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -396,7 +396,7 @@ class AuthService(AuthServiceABC): user.email = update_user_dto.new_auth_user.email # update password - if update_user_dto.change_password and user.password != self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt): + if update_user_dto.new_auth_user.password is not None and update_user_dto.change_password and user.password != self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt): user.password_salt = uuid.uuid4() user.password = self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) From 8e32362333dc1fb4b59d0a9ba716e6dc25309ab9 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 5 Nov 2022 11:51:58 +0100 Subject: [PATCH 099/275] Changed config #70 --- ...ettings.edrafts-pc-ubuntu.json => apisettings.edrafts-pc.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename kdb-bot/src/bot_api/config/{apisettings.edrafts-pc-ubuntu.json => apisettings.edrafts-pc.json} (100%) diff --git a/kdb-bot/src/bot_api/config/apisettings.edrafts-pc-ubuntu.json b/kdb-bot/src/bot_api/config/apisettings.edrafts-pc.json similarity index 100% rename from kdb-bot/src/bot_api/config/apisettings.edrafts-pc-ubuntu.json rename to kdb-bot/src/bot_api/config/apisettings.edrafts-pc.json From e45d156e74740ad7317f5a19fa0c626e74f897c3 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 5 Nov 2022 11:52:31 +0100 Subject: [PATCH 100/275] Changed config #70 --- ...ettings.edrafts-pc-ubuntu.json => appsettings.edrafts-pc.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename kdb-bot/src/bot/config/{appsettings.edrafts-pc-ubuntu.json => appsettings.edrafts-pc.json} (100%) diff --git a/kdb-bot/src/bot/config/appsettings.edrafts-pc-ubuntu.json b/kdb-bot/src/bot/config/appsettings.edrafts-pc.json similarity index 100% rename from kdb-bot/src/bot/config/appsettings.edrafts-pc-ubuntu.json rename to kdb-bot/src/bot/config/appsettings.edrafts-pc.json From 422c9bbb253b76b7a1860095c726c729a19d5c94 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 5 Nov 2022 12:03:22 +0100 Subject: [PATCH 101/275] Fixed log level #70 --- kdb-bot/src/bot/config/appsettings.edrafts-pc.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/bot/config/appsettings.edrafts-pc.json b/kdb-bot/src/bot/config/appsettings.edrafts-pc.json index 9deca017..2e9e52a3 100644 --- a/kdb-bot/src/bot/config/appsettings.edrafts-pc.json +++ b/kdb-bot/src/bot/config/appsettings.edrafts-pc.json @@ -8,7 +8,7 @@ "LoggingSettings": { "Path": "logs/", "Filename": "bot.log", - "ConsoleLogLevel": "DEBUG", + "ConsoleLogLevel": "TRACE", "FileLogLevel": "TRACE" }, "BotLoggingSettings": { From c92403f27481a81a77555eba3582c89e9a6cdcd1 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 5 Nov 2022 12:39:49 +0100 Subject: [PATCH 102/275] Removed unused deps #70 --- kdb-bot/src/bot_api/api_thread.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kdb-bot/src/bot_api/api_thread.py b/kdb-bot/src/bot_api/api_thread.py index f3ecf6cc..f437d140 100644 --- a/kdb-bot/src/bot_api/api_thread.py +++ b/kdb-bot/src/bot_api/api_thread.py @@ -2,8 +2,6 @@ import threading from bot_api.api import Api from bot_api.logging.api_logger import ApiLogger -from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum -from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings class ApiThread(threading.Thread): @@ -11,8 +9,7 @@ class ApiThread(threading.Thread): def __init__( self, logger: ApiLogger, - api: Api, - feature_flags: FeatureFlagsSettings + api: Api ): threading.Thread.__init__(self, daemon=True) From 09a6062992a9f027c1956777f20a183a59b6b8a1 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 5 Nov 2022 12:40:21 +0100 Subject: [PATCH 103/275] Fixed typing #70 --- kdb-bot/src/bot_api/model/discord/server_dto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/bot_api/model/discord/server_dto.py b/kdb-bot/src/bot_api/model/discord/server_dto.py index 07cd4365..a4a5a2da 100644 --- a/kdb-bot/src/bot_api/model/discord/server_dto.py +++ b/kdb-bot/src/bot_api/model/discord/server_dto.py @@ -46,7 +46,7 @@ class ServerDTO(DtoABC): self._server_id = int(values['serverId']) self._discord_id = int(values['discordId']) self._name = values['name'] - self._icon_url = int(values['iconURL']) + self._icon_url = values['iconURL'] def to_dict(self) -> dict: return { From 88f00c604694571465d9b35376d78a9ae9440abc Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 5 Nov 2022 12:42:22 +0100 Subject: [PATCH 104/275] Fixed typing #70 --- kdb-bot/src/bot_data/migration/api_migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/bot_data/migration/api_migration.py b/kdb-bot/src/bot_data/migration/api_migration.py index 53df1192..8f59906a 100644 --- a/kdb-bot/src/bot_data/migration/api_migration.py +++ b/kdb-bot/src/bot_data/migration/api_migration.py @@ -29,7 +29,7 @@ class ApiMigration(MigrationABC): `ForgotPasswordId` VARCHAR(255) DEFAULT NULL, `OAuthId` VARCHAR(255) DEFAULT NULL, `RefreshTokenExpiryTime` DATETIME(6) NOT NULL, - `AuthRole` INT NOT NULL DEFAULT '0', + `AuthRole` INT NOT NULL DEFAULT 0, `CreatedAt` DATETIME(6) NOT NULL, `LastModifiedAt` DATETIME(6) NOT NULL, PRIMARY KEY(`Id`) From e1304cc6023eb152f2a5201281553cbafd946321 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 5 Nov 2022 13:49:01 +0100 Subject: [PATCH 105/275] Added comment #70 --- kdb-bot/src/bot_api/service/auth_service.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index ba45a155..be0859db 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -204,6 +204,7 @@ class AuthService(AuthServiceABC): async def get_auth_user_by_email_async(self, email: str, with_password: bool = False) -> AuthUserDTO: try: + # todo: check if logged in user is admin then send mail user = self._auth_users.get_auth_user_by_email(email) return AUT.to_dto(user, password=user.password if with_password else None) except Exception as e: From e1dbab3f4f4528760bf54dffc5be2b32abf233e8 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 14:25:46 +0100 Subject: [PATCH 106/275] Added level module and migration #25 --- kdb-bot/cpl-workspace.json | 8 +--- .../src/bot/startup_migration_extension.py | 2 + .../configuration/feature_flags_enum.py | 1 + .../src/bot_data/migration/level_migration.py | 33 +++++++++++++ kdb-bot/src/modules/level/__init__.py | 1 + kdb-bot/src/modules/level/level.json | 46 +++++++++++++++++++ kdb-bot/src/modules/level/level_module.py | 19 ++++++++ 7 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 kdb-bot/src/bot_data/migration/level_migration.py create mode 100644 kdb-bot/src/modules/level/__init__.py create mode 100644 kdb-bot/src/modules/level/level.json create mode 100644 kdb-bot/src/modules/level/level_module.py diff --git a/kdb-bot/cpl-workspace.json b/kdb-bot/cpl-workspace.json index ed99de66..37a8b6c6 100644 --- a/kdb-bot/cpl-workspace.json +++ b/kdb-bot/cpl-workspace.json @@ -15,26 +15,22 @@ "bot-api": "src/bot_api/bot-api.json", "get-version": "tools/get_version/get-version.json", "post-build": "tools/post_build/post-build.json", - "set-version": "tools/set_version/set-version.json" + "set-version": "tools/set_version/set-version.json", + "level": "src/modules/level/level.json" }, "Scripts": { - "sv": "cpl set-version", "set-version": "cpl run set-version $ARGS; echo '';", - "gv": "cpl get-version", "get-version": "export VERSION=$(cpl run get-version); echo $VERSION;", - "pre-build": "cpl set-version $ARGS", "post-build": "cpl run post-build", - "pre-prod": "cpl build", "prod": "export KDB_ENVIRONMENT=production; export KDB_NAME=KDB-Prod; cpl start;", "pre-stage": "cpl build", "stage": "export KDB_ENVIRONMENT=staging; export KDB_NAME=KDB-Stage; cpl start;", "pre-dev": "cpl build", "dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;", - "docker-build": "cpl b; docker-compose down; docker build -t kdb-bot/kdb-bot:$(cpl gv) .", "docker-compose": "docker-compose up -d", "docker": "cpl docker-build; cpl docker-compose;" diff --git a/kdb-bot/src/bot/startup_migration_extension.py b/kdb-bot/src/bot/startup_migration_extension.py index 62d4d39d..a5d75b6d 100644 --- a/kdb-bot/src/bot/startup_migration_extension.py +++ b/kdb-bot/src/bot/startup_migration_extension.py @@ -7,6 +7,7 @@ from bot_data.abc.migration_abc import MigrationABC from bot_data.migration.api_migration import ApiMigration from bot_data.migration.auto_role_migration import AutoRoleMigration from bot_data.migration.initial_migration import InitialMigration +from bot_data.migration.level_migration import LevelMigration from bot_data.service.migration_service import MigrationService @@ -23,3 +24,4 @@ class StartupMigrationExtension(StartupExtensionABC): services.add_transient(MigrationABC, InitialMigration) services.add_transient(MigrationABC, AutoRoleMigration) # 03.10.2022 #54 - 0.2.2 services.add_transient(MigrationABC, ApiMigration) # 15.10.2022 #70 - 0.3.0 + services.add_transient(MigrationABC, LevelMigration) # 06.11.2022 #25 - 0.3.0 diff --git a/kdb-bot/src/bot_core/configuration/feature_flags_enum.py b/kdb-bot/src/bot_core/configuration/feature_flags_enum.py index b41ec6e5..226ca1d8 100644 --- a/kdb-bot/src/bot_core/configuration/feature_flags_enum.py +++ b/kdb-bot/src/bot_core/configuration/feature_flags_enum.py @@ -13,6 +13,7 @@ class FeatureFlagsEnum(Enum): core_extension_module = 'CoreExtensionModule' data_module = 'DataModule', database_module = 'DatabaseModule', + level_module = 'LevelModule' moderator_module = 'ModeratorModule' permission_module = 'PermissionModule' # features diff --git a/kdb-bot/src/bot_data/migration/level_migration.py b/kdb-bot/src/bot_data/migration/level_migration.py new file mode 100644 index 00000000..d01b5677 --- /dev/null +++ b/kdb-bot/src/bot_data/migration/level_migration.py @@ -0,0 +1,33 @@ +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.migration_abc import MigrationABC +from bot_data.db_context import DBContext + + +class LevelMigration(MigrationABC): + name = '0.3_LevelMigration' + + def __init__(self, logger: DatabaseLogger, db: DBContext): + MigrationABC.__init__(self) + self._logger = logger + self._db = db + self._cursor = db.cursor + + def upgrade(self): + self._logger.debug(__name__, 'Running upgrade') + + self._cursor.execute( + str(f""" + CREATE TABLE IF NOT EXISTS `Levels` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `Name` VARCHAR(255) NOT NULL, + `PermissionInt` BIGINT NOT NULL, + `ServerId` BIGINT, + PRIMARY KEY(`Id`), + FOREIGN KEY (`ServerId`) REFERENCES `Servers`(`ServerId`) + ); + """) + ) + + def downgrade(self): + self._cursor.execute('DROP TABLE `Levels`;') + diff --git a/kdb-bot/src/modules/level/__init__.py b/kdb-bot/src/modules/level/__init__.py new file mode 100644 index 00000000..ad5eca30 --- /dev/null +++ b/kdb-bot/src/modules/level/__init__.py @@ -0,0 +1 @@ +# imports: diff --git a/kdb-bot/src/modules/level/level.json b/kdb-bot/src/modules/level/level.json new file mode 100644 index 00000000..1d8d7425 --- /dev/null +++ b/kdb-bot/src/modules/level/level.json @@ -0,0 +1,46 @@ +{ + "ProjectSettings": { + "Name": "level", + "Version": { + "Major": "0", + "Minor": "0", + "Micro": "0" + }, + "Author": "", + "AuthorEmail": "", + "Description": "", + "LongDescription": "", + "URL": "", + "CopyrightDate": "", + "CopyrightName": "", + "LicenseName": "", + "LicenseDescription": "", + "Dependencies": [ + "cpl-core>=2022.10.0.post7" + ], + "DevDependencies": [ + "cpl-cli>=2022.10.1" + ], + "PythonVersion": ">=3.10.4", + "PythonPath": { + "linux": "" + }, + "Classifiers": [] + }, + "BuildSettings": { + "ProjectType": "library", + "SourcePath": "", + "OutputPath": "../../dist", + "Main": "level.main", + "EntryPoint": "level", + "IncludePackageData": false, + "Included": [], + "Excluded": [ + "*/__pycache__", + "*/logs", + "*/tests" + ], + "PackageData": {}, + "ProjectReferences": [] + } +} \ No newline at end of file diff --git a/kdb-bot/src/modules/level/level_module.py b/kdb-bot/src/modules/level/level_module.py new file mode 100644 index 00000000..dfdd9bda --- /dev/null +++ b/kdb-bot/src/modules/level/level_module.py @@ -0,0 +1,19 @@ +from cpl_core.configuration import ConfigurationABC +from cpl_core.dependency_injection import ServiceCollectionABC +from cpl_core.environment import ApplicationEnvironmentABC +from cpl_discord.service.discord_collection_abc import DiscordCollectionABC + +from bot_core.abc.module_abc import ModuleABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum + + +class LevelModule(ModuleABC): + + def __init__(self, dc: DiscordCollectionABC): + ModuleABC.__init__(self, dc, FeatureFlagsEnum.level_module) + + def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): + pass + + def configure_services(self, service: ServiceCollectionABC, env: ApplicationEnvironmentABC): + pass From 5a3eb57c0b99623ca068797d7df476647bf86628 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 14:35:28 +0100 Subject: [PATCH 107/275] Added level model #25 --- .../src/bot_data/migration/level_migration.py | 2 + kdb-bot/src/bot_data/model/level.py | 116 ++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 kdb-bot/src/bot_data/model/level.py diff --git a/kdb-bot/src/bot_data/migration/level_migration.py b/kdb-bot/src/bot_data/migration/level_migration.py index d01b5677..71d618ae 100644 --- a/kdb-bot/src/bot_data/migration/level_migration.py +++ b/kdb-bot/src/bot_data/migration/level_migration.py @@ -20,6 +20,8 @@ class LevelMigration(MigrationABC): CREATE TABLE IF NOT EXISTS `Levels` ( `Id` BIGINT NOT NULL AUTO_INCREMENT, `Name` VARCHAR(255) NOT NULL, + `Color` VARCHAR(7) NOT NULL, + `MinXp` BIGINT NOT NULL, `PermissionInt` BIGINT NOT NULL, `ServerId` BIGINT, PRIMARY KEY(`Id`), diff --git a/kdb-bot/src/bot_data/model/level.py b/kdb-bot/src/bot_data/model/level.py new file mode 100644 index 00000000..abd7e687 --- /dev/null +++ b/kdb-bot/src/bot_data/model/level.py @@ -0,0 +1,116 @@ +from datetime import datetime +from typing import Optional +from cpl_core.database import TableABC + +from bot_data.model.server import Server + + +class Level(TableABC): + + def __init__(self, name: str, color: str, min_xp: int, permissions: int, server: Optional[Server], created_at: datetime = None, modified_at: datetime = None, id=0): + self._id = id + self._name = name + self._color = color + self._min_xp = min_xp + self._permissions = permissions + self._server = server + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def id(self) -> int: + return self._id + + @property + def name(self) -> str: + return self._name + + @name.setter + def name(self, value: str): + self._name = value + + @property + def color(self) -> str: + return self._color + + @color.setter + def color(self, value: str): + self._color = value + + @property + def min_xp(self) -> int: + return self._min_xp + + @min_xp.setter + def min_xp(self, value: int): + self._min_xp = value + + @property + def permissions(self) -> int: + return self._permissions + + @permissions.setter + def permissions(self, value: int): + self._permissions = value + + @property + def server(self) -> Optional[Server]: + return self._server + + @staticmethod + def get_select_all_string() -> str: + return str(f""" + SELECT * FROM `Levels`; + """) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `Levels` + WHERE `Id` = {id}; + """) + + @staticmethod + def get_select_by_server_id_string(dc_id: int, s_id: int) -> str: + return str(f""" + SELECT * FROM `Levels` + WHERE `ServerId` = {s_id}; + """) + + @property + def insert_string(self) -> str: + return str(f""" + INSERT INTO `Levels` ( + `Name`, `Color`, `MinXp`, `PermissionInt`, `ServerId`, `CreatedAt`, `LastModifiedAt` + ) VALUES ( + `{self._name}`, + `{self._color}`, + {self._min_xp}, + {self._permissions}, + {self._server.server_id}, + '{self._created_at}', + '{self._modified_at}' + ); + """) + + @property + def udpate_string(self) -> str: + return str(f""" + UPDATE `Levels` + SET `Name` = {self._name}, + `Name` = {self._name}, + `Color` = {self._color}, + `MinXp` = {self._min_xp}, + `PermissionInt` = {self._permissions}, + `LastModifiedAt` = '{self._modified_at}' + WHERE `Id` = {self._id}; + """) + + @property + def delete_string(self) -> str: + return str(f""" + DELETE FROM `Levels` + WHERE `Id` = {self._id}; + """) From e6e26b77f90a3f5a4d860e7452391fd7163ffa88 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 14:46:11 +0100 Subject: [PATCH 108/275] Added level repo #25 --- .../src/bot_data/abc/level_repository_abc.py | 36 +++++++ kdb-bot/src/bot_data/data_module.py | 3 + kdb-bot/src/bot_data/model/level.py | 2 +- .../service/level_repository_service.py | 97 +++++++++++++++++++ 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 kdb-bot/src/bot_data/abc/level_repository_abc.py create mode 100644 kdb-bot/src/bot_data/service/level_repository_service.py diff --git a/kdb-bot/src/bot_data/abc/level_repository_abc.py b/kdb-bot/src/bot_data/abc/level_repository_abc.py new file mode 100644 index 00000000..89a2a67f --- /dev/null +++ b/kdb-bot/src/bot_data/abc/level_repository_abc.py @@ -0,0 +1,36 @@ +from abc import ABC, abstractmethod +from typing import Optional + +from cpl_query.extension import List + +from bot_data.model.level import Level + + +class LevelRepositoryABC(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + def get_levels(self) -> List[Level]: pass + + @abstractmethod + def get_level_by_id(self, id: int) -> Level: pass + + @abstractmethod + def find_level_by_id(self, id: int) -> Optional[Level]: pass + + @abstractmethod + def get_levels_by_server_id(self, server_id: int) -> List[Level]: pass + + @abstractmethod + def find_levels_by_server_id(self, server_id: int) -> Optional[List[Level]]: pass + + @abstractmethod + def add_level(self, level: Level): pass + + @abstractmethod + def update_level(self, level: Level): pass + + @abstractmethod + def delete_level(self, level: Level): pass diff --git a/kdb-bot/src/bot_data/data_module.py b/kdb-bot/src/bot_data/data_module.py index 7537b6f2..bf8786e5 100644 --- a/kdb-bot/src/bot_data/data_module.py +++ b/kdb-bot/src/bot_data/data_module.py @@ -9,6 +9,7 @@ from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.client_repository_abc import ClientRepositoryABC from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC +from bot_data.abc.level_repository_abc import LevelRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC @@ -17,6 +18,7 @@ from bot_data.service.auth_user_repository_service import AuthUserRepositoryServ from bot_data.service.auto_role_repository_service import AutoRoleRepositoryService from bot_data.service.client_repository_service import ClientRepositoryService from bot_data.service.known_user_repository_service import KnownUserRepositoryService +from bot_data.service.level_repository_service import LevelRepositoryService from bot_data.service.server_repository_service import ServerRepositoryService from bot_data.service.user_joined_server_repository_service import UserJoinedServerRepositoryService from bot_data.service.user_joined_voice_channel_service import UserJoinedVoiceChannelRepositoryService @@ -40,3 +42,4 @@ class DataModule(ModuleABC): services.add_transient(UserJoinedServerRepositoryABC, UserJoinedServerRepositoryService) services.add_transient(UserJoinedVoiceChannelRepositoryABC, UserJoinedVoiceChannelRepositoryService) services.add_transient(AutoRoleRepositoryABC, AutoRoleRepositoryService) + services.add_transient(LevelRepositoryABC, LevelRepositoryService) diff --git a/kdb-bot/src/bot_data/model/level.py b/kdb-bot/src/bot_data/model/level.py index abd7e687..fcde27bf 100644 --- a/kdb-bot/src/bot_data/model/level.py +++ b/kdb-bot/src/bot_data/model/level.py @@ -73,7 +73,7 @@ class Level(TableABC): """) @staticmethod - def get_select_by_server_id_string(dc_id: int, s_id: int) -> str: + def get_select_by_server_id_string(s_id: int) -> str: return str(f""" SELECT * FROM `Levels` WHERE `ServerId` = {s_id}; diff --git a/kdb-bot/src/bot_data/service/level_repository_service.py b/kdb-bot/src/bot_data/service/level_repository_service.py new file mode 100644 index 00000000..37ee5952 --- /dev/null +++ b/kdb-bot/src/bot_data/service/level_repository_service.py @@ -0,0 +1,97 @@ +from typing import Optional + +from cpl_core.database.context import DatabaseContextABC +from cpl_query.extension import List + +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.level_repository_abc import LevelRepositoryABC +from bot_data.model.level import Level + + +class LevelRepositoryService(LevelRepositoryABC): + + def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC, servers: ServerRepositoryABC): + self._logger = logger + self._context = db_context + + self._servers = servers + + LevelRepositoryABC.__init__(self) + + @staticmethod + def _get_value_from_result(value: any) -> Optional[any]: + if isinstance(value, str) and 'NULL' in value: + return None + + return value + + def _level_from_result(self, sql_result: tuple) -> Level: + return Level( + self._get_value_from_result(sql_result[1]), # name + self._get_value_from_result(sql_result[2]), # color + int(self._get_value_from_result(sql_result[3])), # min xp + int(self._get_value_from_result(sql_result[4])), # permissions + self._servers.get_server_by_id(sql_result[5]), # server + id=self._get_value_from_result(sql_result[0]) # id + ) + + def get_levels(self) -> List[Level]: + levels = List(Level) + self._logger.trace(__name__, f'Send SQL command: {Level.get_select_all_string()}') + results = self._context.select(Level.get_select_all_string()) + for result in results: + self._logger.trace(__name__, f'Get level with id {result[0]}') + levels.append(self._level_from_result(result)) + + return levels + + def get_level_by_id(self, id: int) -> Level: + self._logger.trace(__name__, f'Send SQL command: {Level.get_select_by_id_string(id)}') + result = self._context.select(Level.get_select_by_id_string(id))[0] + + return self._level_from_result(result) + + def find_level_by_id(self, id: int) -> Optional[Level]: + self._logger.trace(__name__, f'Send SQL command: {Level.get_select_by_id_string(id)}') + result = self._context.select(Level.get_select_by_id_string(id)) + if result is None or len(result) == 0: + return None + + return self._level_from_result(result[0]) + + def get_levels_by_server_id(self, server_id: int) -> List[Level]: + levels = List(Level) + self._logger.trace(__name__, f'Send SQL command: {Level.get_select_by_server_id_string(server_id)}') + results = self._context.select(Level.get_select_by_server_id_string(server_id))[0] + + for result in results: + self._logger.trace(__name__, f'Get level with id {result[0]}') + levels.append(self._level_from_result(result)) + + return levels + + def find_levels_by_server_id(self, server_id: int) -> Optional[List[Level]]: + levels = List(Level) + self._logger.trace(__name__, f'Send SQL command: {Level.get_select_by_server_id_string(server_id)}') + results = self._context.select(Level.get_select_by_server_id_string(server_id)) + if results is None or len(results) == 0: + return None + + for result in results: + self._logger.trace(__name__, f'Get level with id {result[0]}') + levels.append(self._level_from_result(result)) + + return levels + + def add_level(self, level: Level): + self._logger.trace(__name__, f'Send SQL command: {level.insert_string}') + self._context.cursor.execute(level.insert_string) + + def update_level(self, level: Level): + self._logger.trace(__name__, f'Send SQL command: {level.udpate_string}') + self._context.cursor.execute(level.udpate_string) + + def delete_level(self, level: Level): + self._logger.trace(__name__, f'Send SQL command: {level.delete_string}') + self._context.cursor.execute(level.delete_string) From 666c527730fe600ebaf1b8a9ae1206a32b836ee6 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 16:26:09 +0100 Subject: [PATCH 109/275] Added logic to handle default levels #25 --- kdb-bot/src/bot/config/feature-flags.json | 3 +- kdb-bot/src/bot/module_list.py | 2 + kdb-bot/src/bot_data/abc/data_seeder_abc.py | 10 ++++ kdb-bot/src/bot_data/data_module.py | 3 ++ .../src/bot_data/migration/level_migration.py | 4 +- kdb-bot/src/bot_data/model/level.py | 15 +++--- .../src/bot_data/service/seeder_service.py | 25 ++++++++++ .../modules/database/database_extension.py | 1 + .../database/database_on_ready_event.py | 5 ++ .../modules/level/configuration/__init__.py | 1 + .../level/configuration/level_settings.py | 33 +++++++++++++ kdb-bot/src/modules/level/default-level.json | 28 +++++++++++ kdb-bot/src/modules/level/level_module.py | 12 +++-- kdb-bot/src/modules/level/level_seeder.py | 46 +++++++++++++++++++ 14 files changed, 177 insertions(+), 11 deletions(-) create mode 100644 kdb-bot/src/bot_data/abc/data_seeder_abc.py create mode 100644 kdb-bot/src/bot_data/service/seeder_service.py create mode 100644 kdb-bot/src/modules/level/configuration/__init__.py create mode 100644 kdb-bot/src/modules/level/configuration/level_settings.py create mode 100644 kdb-bot/src/modules/level/default-level.json create mode 100644 kdb-bot/src/modules/level/level_seeder.py diff --git a/kdb-bot/src/bot/config/feature-flags.json b/kdb-bot/src/bot/config/feature-flags.json index 68bf6e00..da07fa23 100644 --- a/kdb-bot/src/bot/config/feature-flags.json +++ b/kdb-bot/src/bot/config/feature-flags.json @@ -1,6 +1,6 @@ { "FeatureFlags": { - "ApiModule": true, + "ApiModule": false, "AdminModule": true, "AutoRoleModule": true, "BaseModule": true, @@ -9,6 +9,7 @@ "CoreExtensionModule": true, "DatabaseModule": true, "ModeratorModule": true, + "LevelModule": true, "PermissionModule": true, "PresenceModule": true } diff --git a/kdb-bot/src/bot/module_list.py b/kdb-bot/src/bot/module_list.py index b818ad4b..03b8307c 100644 --- a/kdb-bot/src/bot/module_list.py +++ b/kdb-bot/src/bot/module_list.py @@ -9,6 +9,7 @@ from modules.auto_role.auto_role_module import AutoRoleModule from modules.base.base_module import BaseModule from modules.boot_log.boot_log_module import BootLogModule from modules.database.database_module import DatabaseModule +from modules.level.level_module import LevelModule from modules.moderator.moderator_module import ModeratorModule from modules.permission.permission_module import PermissionModule @@ -25,6 +26,7 @@ class ModuleList: AutoRoleModule, BaseModule, DatabaseModule, + LevelModule, ModeratorModule, PermissionModule, ApiModule, diff --git a/kdb-bot/src/bot_data/abc/data_seeder_abc.py b/kdb-bot/src/bot_data/abc/data_seeder_abc.py new file mode 100644 index 00000000..a4fecdd7 --- /dev/null +++ b/kdb-bot/src/bot_data/abc/data_seeder_abc.py @@ -0,0 +1,10 @@ +from abc import ABC, abstractmethod + + +class DataSeederABC(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + def seed(self): pass diff --git a/kdb-bot/src/bot_data/data_module.py b/kdb-bot/src/bot_data/data_module.py index bf8786e5..0668bffe 100644 --- a/kdb-bot/src/bot_data/data_module.py +++ b/kdb-bot/src/bot_data/data_module.py @@ -19,6 +19,7 @@ from bot_data.service.auto_role_repository_service import AutoRoleRepositoryServ from bot_data.service.client_repository_service import ClientRepositoryService from bot_data.service.known_user_repository_service import KnownUserRepositoryService from bot_data.service.level_repository_service import LevelRepositoryService +from bot_data.service.seeder_service import SeederService from bot_data.service.server_repository_service import ServerRepositoryService from bot_data.service.user_joined_server_repository_service import UserJoinedServerRepositoryService from bot_data.service.user_joined_voice_channel_service import UserJoinedVoiceChannelRepositoryService @@ -43,3 +44,5 @@ class DataModule(ModuleABC): services.add_transient(UserJoinedVoiceChannelRepositoryABC, UserJoinedVoiceChannelRepositoryService) services.add_transient(AutoRoleRepositoryABC, AutoRoleRepositoryService) services.add_transient(LevelRepositoryABC, LevelRepositoryService) + + services.add_transient(SeederService) diff --git a/kdb-bot/src/bot_data/migration/level_migration.py b/kdb-bot/src/bot_data/migration/level_migration.py index 71d618ae..5b1dbc0f 100644 --- a/kdb-bot/src/bot_data/migration/level_migration.py +++ b/kdb-bot/src/bot_data/migration/level_migration.py @@ -20,10 +20,12 @@ class LevelMigration(MigrationABC): CREATE TABLE IF NOT EXISTS `Levels` ( `Id` BIGINT NOT NULL AUTO_INCREMENT, `Name` VARCHAR(255) NOT NULL, - `Color` VARCHAR(7) NOT NULL, + `Color` VARCHAR(8) NOT NULL, `MinXp` BIGINT NOT NULL, `PermissionInt` BIGINT NOT NULL, `ServerId` BIGINT, + `CreatedAt` DATETIME(6), + `LastModifiedAt` DATETIME(6), PRIMARY KEY(`Id`), FOREIGN KEY (`ServerId`) REFERENCES `Servers`(`ServerId`) ); diff --git a/kdb-bot/src/bot_data/model/level.py b/kdb-bot/src/bot_data/model/level.py index fcde27bf..9cfc63f1 100644 --- a/kdb-bot/src/bot_data/model/level.py +++ b/kdb-bot/src/bot_data/model/level.py @@ -56,8 +56,12 @@ class Level(TableABC): self._permissions = value @property - def server(self) -> Optional[Server]: + def server(self) -> Server: return self._server + + @server.setter + def server(self, value: Server): + self._server = value @staticmethod def get_select_all_string() -> str: @@ -85,8 +89,8 @@ class Level(TableABC): INSERT INTO `Levels` ( `Name`, `Color`, `MinXp`, `PermissionInt`, `ServerId`, `CreatedAt`, `LastModifiedAt` ) VALUES ( - `{self._name}`, - `{self._color}`, + '{self._name}', + '{self._color}', {self._min_xp}, {self._permissions}, {self._server.server_id}, @@ -99,9 +103,8 @@ class Level(TableABC): def udpate_string(self) -> str: return str(f""" UPDATE `Levels` - SET `Name` = {self._name}, - `Name` = {self._name}, - `Color` = {self._color}, + SET `Name` = '{self._name}', + `Color` = '{self._color}', `MinXp` = {self._min_xp}, `PermissionInt` = {self._permissions}, `LastModifiedAt` = '{self._modified_at}' diff --git a/kdb-bot/src/bot_data/service/seeder_service.py b/kdb-bot/src/bot_data/service/seeder_service.py new file mode 100644 index 00000000..6d0f9b7d --- /dev/null +++ b/kdb-bot/src/bot_data/service/seeder_service.py @@ -0,0 +1,25 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_core.dependency_injection import ServiceProviderABC +from cpl_query.extension import List + +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.data_seeder_abc import DataSeederABC + + +class SeederService: + + def __init__(self, logger: DatabaseLogger, services: ServiceProviderABC, db: DatabaseContextABC): + self._logger = logger + self._services = services + + self._db = db + + self._seeder = List(type, DataSeederABC.__subclasses__()) + + async def seed(self): + self._logger.info(__name__, f"Seed data") + for seeder in self._seeder: + seeder_as_service: DataSeederABC = self._services.get_service(seeder) + self._logger.debug(__name__, f"Starting seeder {seeder.__name__}") + await seeder_as_service.seed() + self._db.save_changes() diff --git a/kdb-bot/src/modules/database/database_extension.py b/kdb-bot/src/modules/database/database_extension.py index 8bc111e9..1cc7a77f 100644 --- a/kdb-bot/src/modules/database/database_extension.py +++ b/kdb-bot/src/modules/database/database_extension.py @@ -9,6 +9,7 @@ from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.logging.database_logger import DatabaseLogger from bot_data.service.migration_service import MigrationService +from bot_data.service.seeder_service import SeederService class DatabaseExtension(ApplicationExtensionABC): diff --git a/kdb-bot/src/modules/database/database_on_ready_event.py b/kdb-bot/src/modules/database/database_on_ready_event.py index 9176a26c..96f013ee 100644 --- a/kdb-bot/src/modules/database/database_on_ready_event.py +++ b/kdb-bot/src/modules/database/database_on_ready_event.py @@ -20,6 +20,7 @@ from bot_data.model.server import Server from bot_data.model.user import User from bot_data.model.user_joined_server import UserJoinedServer from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel +from bot_data.service.seeder_service import SeederService from bot_data.service.user_repository_service import ServerRepositoryABC from modules.base.configuration.base_server_settings import BaseServerSettings @@ -30,6 +31,7 @@ class DatabaseOnReadyEvent(OnReadyABC): self, config: ConfigurationABC, logger: DatabaseLogger, + seeder: SeederService, bot: DiscordBotServiceABC, db_context: DatabaseContextABC, server_repo: ServerRepositoryABC, @@ -43,6 +45,7 @@ class DatabaseOnReadyEvent(OnReadyABC): self._config = config self._logger = logger + self._seeder = seeder self._bot = bot self._db_context = db_context self._servers = server_repo @@ -303,6 +306,8 @@ class DatabaseOnReadyEvent(OnReadyABC): async def on_ready(self): self._logger.debug(__name__, f'Module {type(self)} started') + await self._seeder.seed() + self._check_known_users() self._check_servers() self._check_clients() diff --git a/kdb-bot/src/modules/level/configuration/__init__.py b/kdb-bot/src/modules/level/configuration/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/kdb-bot/src/modules/level/configuration/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/level/configuration/level_settings.py b/kdb-bot/src/modules/level/configuration/level_settings.py new file mode 100644 index 00000000..95dda2c8 --- /dev/null +++ b/kdb-bot/src/modules/level/configuration/level_settings.py @@ -0,0 +1,33 @@ +import traceback + +from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC +from cpl_core.console import Console +from cpl_query.extension import List + +from bot_data.model.level import Level + + +class LevelSettings(ConfigurationModelABC): + + def __init__(self): + ConfigurationModelABC.__init__(self) + + self._levels = List(Level) + + @property + def levels(self) -> List[Level]: + return self._levels + + def from_dict(self, settings: dict): + try: + for level in settings: + self._levels.append(Level( + level['Name'], + level['Color'], + int(level['MinXp']), + int(level['Permissions']), + None + )) + except Exception as e: + Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') + Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') diff --git a/kdb-bot/src/modules/level/default-level.json b/kdb-bot/src/modules/level/default-level.json new file mode 100644 index 00000000..04cb8745 --- /dev/null +++ b/kdb-bot/src/modules/level/default-level.json @@ -0,0 +1,28 @@ +{ + "Level": [ + { + "Name": "Newbie", + "Color": "0x1abc9c", + "MinXp": 0, + "Permissions": 968552209984 + }, + { + "Name": "Keks", + "Color": "0x2ecc71", + "MinXp": 100, + "Permissions": 1002928856640 + }, + { + "Name": "Doppelkeks", + "Color": "0x3498db", + "MinXp": 200, + "Permissions": 1071849660224 + }, + { + "Name": "Auror", + "Color": "0xf1c40f", + "MinXp": 300, + "Permissions": 1089042120513 + } + ] +} \ No newline at end of file diff --git a/kdb-bot/src/modules/level/level_module.py b/kdb-bot/src/modules/level/level_module.py index dfdd9bda..56529743 100644 --- a/kdb-bot/src/modules/level/level_module.py +++ b/kdb-bot/src/modules/level/level_module.py @@ -1,3 +1,5 @@ +import os + from cpl_core.configuration import ConfigurationABC from cpl_core.dependency_injection import ServiceCollectionABC from cpl_core.environment import ApplicationEnvironmentABC @@ -5,6 +7,7 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from modules.level.level_seeder import LevelSeeder class LevelModule(ModuleABC): @@ -13,7 +16,10 @@ class LevelModule(ModuleABC): ModuleABC.__init__(self, dc, FeatureFlagsEnum.level_module) def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): - pass + cwd = env.working_directory + env.set_working_directory(os.path.dirname(os.path.realpath(__file__))) + config.add_json_file(f'default-level.json', optional=False) + env.set_working_directory(cwd) - def configure_services(self, service: ServiceCollectionABC, env: ApplicationEnvironmentABC): - pass + def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): + services.add_transient(LevelSeeder) diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py new file mode 100644 index 00000000..a8bcd5db --- /dev/null +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -0,0 +1,46 @@ +from cpl_discord.service import DiscordBotServiceABC +from discord import Permissions, Colour + +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.data_seeder_abc import DataSeederABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.service.level_repository_service import LevelRepositoryService +from modules.level.configuration.level_settings import LevelSettings + + +class LevelSeeder(DataSeederABC): + + def __init__(self, logger: DatabaseLogger, levels: LevelSettings, level_repo: LevelRepositoryService, servers: ServerRepositoryABC, bot: DiscordBotServiceABC): + DataSeederABC.__init__(self) + + self._logger = logger + self._default_levels = levels.levels.order_by_descending(lambda l: l.min_xp) + self._levels = level_repo + self._servers = servers + self._bot = bot + + async def seed(self): + if self._levels.get_levels().count() > 0: + return + + for guild in self._bot.guilds: + server = self._servers.find_server_by_discord_id(guild.id) + if server is None: + continue + + levels = self._levels.find_levels_by_server_id(server.server_id) + if levels is not None and levels.count() > 0: + continue + + for level in self._default_levels: + try: + if guild.roles.where(lambda r: r.name == level.name).count() == 0: + await guild.create_role(name=level.name, colour=Colour(int(level.color, 16)), hoist=False, mentionable=True, permissions=Permissions(level.permissions)) + + self._logger.info(__name__, f'Created default level {level.name}') + level.server = server + self._levels.add_level(level) + except Exception as e: + self._logger.error(__name__, f'Creating default role failed', e) + + self._logger.debug(__name__, f'Seeded default levels') From 861a84708824ecb7f641e857ecffcc86ef42a7b6 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 19:18:11 +0100 Subject: [PATCH 110/275] Improved logic to create roles for levels by default #25 --- .../bot/config/appsettings.edrafts-pc.json | 2 +- kdb-bot/src/modules/level/level_seeder.py | 67 +++++++++++++++---- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/kdb-bot/src/bot/config/appsettings.edrafts-pc.json b/kdb-bot/src/bot/config/appsettings.edrafts-pc.json index 2e9e52a3..9deca017 100644 --- a/kdb-bot/src/bot/config/appsettings.edrafts-pc.json +++ b/kdb-bot/src/bot/config/appsettings.edrafts-pc.json @@ -8,7 +8,7 @@ "LoggingSettings": { "Path": "logs/", "Filename": "bot.log", - "ConsoleLogLevel": "TRACE", + "ConsoleLogLevel": "DEBUG", "FileLogLevel": "TRACE" }, "BotLoggingSettings": { diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py index a8bcd5db..8193f474 100644 --- a/kdb-bot/src/modules/level/level_seeder.py +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -1,9 +1,13 @@ +from cpl_discord.container import Guild, Role from cpl_discord.service import DiscordBotServiceABC +from cpl_query.extension import List from discord import Permissions, Colour from bot_core.logging.database_logger import DatabaseLogger from bot_data.abc.data_seeder_abc import DataSeederABC from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.model.level import Level +from bot_data.model.server import Server from bot_data.service.level_repository_service import LevelRepositoryService from modules.level.configuration.level_settings import LevelSettings @@ -19,8 +23,39 @@ class LevelSeeder(DataSeederABC): self._servers = servers self._bot = bot + async def _create_level(self, level: Level, guild: Guild, server: Server): + try: + if guild.roles.where(lambda r: r.name == level.name).count() != 0: + return + + await guild.create_role(name=level.name, colour=Colour(int(level.color, 16)), hoist=False, mentionable=True, permissions=Permissions(level.permissions)) + self._logger.info(__name__, f'Created level {level.name}') + level.server = server + self._levels.add_level(level) + except Exception as e: + self._logger.error(__name__, f'Creating level failed', e) + async def seed(self): - if self._levels.get_levels().count() > 0: + created_default = False + for guild in self._bot.guilds: + server = self._servers.find_server_by_discord_id(guild.id) + if server is None: + continue + + levels = self._levels.find_levels_by_server_id(server.server_id) + if levels is not None and levels.count() > 0: + for level in levels: + await self._create_level(level, guild, server) + + continue + + for level in self._default_levels: + created_default = True + await self._create_level(level, guild, server) + + self._logger.debug(__name__, f'Seeded default levels') + + if created_default: return for guild in self._bot.guilds: @@ -29,18 +64,26 @@ class LevelSeeder(DataSeederABC): continue levels = self._levels.find_levels_by_server_id(server.server_id) - if levels is not None and levels.count() > 0: - continue + roles: List[Role] = List(Role) + for role in guild.roles.order_by_descending(lambda r: r.position): + if levels.where(lambda l: l.name == role.name).count() == 0: + continue + + roles.append(role) + + levels = levels.order_by(lambda l: l.min_xp) + position_below_levels = roles.where(lambda r: r.name == levels.order_by(lambda l: l.min_xp).first().name).single().position - 1 + for role in roles: + new_position = position_below_levels + (levels.index(levels.where(lambda l: l.name == role.name).single()) - 1) + if new_position <= 0: + above_role: Role = roles.where(lambda r: r.position == 1).single() + await above_role.edit(position=above_role.position + 1) + new_position = 1 - for level in self._default_levels: try: - if guild.roles.where(lambda r: r.name == level.name).count() == 0: - await guild.create_role(name=level.name, colour=Colour(int(level.color, 16)), hoist=False, mentionable=True, permissions=Permissions(level.permissions)) - - self._logger.info(__name__, f'Created default level {level.name}') - level.server = server - self._levels.add_level(level) + self._logger.debug(__name__, f'Moved {role.name} from {role.position} to {new_position}') + await role.edit(position=new_position) except Exception as e: - self._logger.error(__name__, f'Creating default role failed', e) + self._logger.error(__name__, f'Cannot change position of {role.name}', e) - self._logger.debug(__name__, f'Seeded default levels') + self._logger.debug(__name__, f'Checked role order') From 8118d4edc3f68c86f84ca05d96a6d51df5073e5b Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 19:36:15 +0100 Subject: [PATCH 111/275] Improved logic to create roles for levels by default #25 --- kdb-bot/src/modules/level/level_seeder.py | 46 ++++++++++------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py index 8193f474..e7329126 100644 --- a/kdb-bot/src/modules/level/level_seeder.py +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -25,7 +25,7 @@ class LevelSeeder(DataSeederABC): async def _create_level(self, level: Level, guild: Guild, server: Server): try: - if guild.roles.where(lambda r: r.name == level.name).count() != 0: + if guild.roles.where(lambda r: r.name == level.name).count() > 0: return await guild.create_role(name=level.name, colour=Colour(int(level.color, 16)), hoist=False, mentionable=True, permissions=Permissions(level.permissions)) @@ -36,50 +36,42 @@ class LevelSeeder(DataSeederABC): self._logger.error(__name__, f'Creating level failed', e) async def seed(self): - created_default = False + # create levels for guild in self._bot.guilds: + created_default = False + if guild.roles.where(lambda r: r.name == '___ Level ___').count() == 0: + await guild.create_role(name='___ Level ___') + server = self._servers.find_server_by_discord_id(guild.id) if server is None: continue levels = self._levels.find_levels_by_server_id(server.server_id) if levels is not None and levels.count() > 0: + # create levels from db for level in levels: await self._create_level(level, guild, server) + self._logger.debug(__name__, f'Seeded levels') + else: + # create default levels + for level in self._default_levels: + created_default = True + await self._create_level(level, guild, server) + + self._logger.debug(__name__, f'Seeded default levels') + + if created_default: continue - for level in self._default_levels: - created_default = True - await self._create_level(level, guild, server) - - self._logger.debug(__name__, f'Seeded default levels') - - if created_default: - return - - for guild in self._bot.guilds: - server = self._servers.find_server_by_discord_id(guild.id) - if server is None: - continue - - levels = self._levels.find_levels_by_server_id(server.server_id) - roles: List[Role] = List(Role) + position_above_levels = guild.roles.where(lambda r: r.name == '~~~ Level ~~~').single().position for role in guild.roles.order_by_descending(lambda r: r.position): if levels.where(lambda l: l.name == role.name).count() == 0: continue - roles.append(role) - - levels = levels.order_by(lambda l: l.min_xp) - position_below_levels = roles.where(lambda r: r.name == levels.order_by(lambda l: l.min_xp).first().name).single().position - 1 - for role in roles: - new_position = position_below_levels + (levels.index(levels.where(lambda l: l.name == role.name).single()) - 1) + new_position = position_above_levels - (levels.index(levels.where(lambda l: l.name == role.name).single()) + 1) if new_position <= 0: - above_role: Role = roles.where(lambda r: r.position == 1).single() - await above_role.edit(position=above_role.position + 1) new_position = 1 - try: self._logger.debug(__name__, f'Moved {role.name} from {role.position} to {new_position}') await role.edit(position=new_position) From 8aee72856c31d35e3d6522a423914360b13a9c43 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 19:51:43 +0100 Subject: [PATCH 112/275] Added level service #25 --- kdb-bot/src/modules/level/level_module.py | 2 + kdb-bot/src/modules/level/service/__init__.py | 1 + .../modules/level/service/level_service.py | 53 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 kdb-bot/src/modules/level/service/__init__.py create mode 100644 kdb-bot/src/modules/level/service/level_service.py diff --git a/kdb-bot/src/modules/level/level_module.py b/kdb-bot/src/modules/level/level_module.py index 56529743..760544ba 100644 --- a/kdb-bot/src/modules/level/level_module.py +++ b/kdb-bot/src/modules/level/level_module.py @@ -8,6 +8,7 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from modules.level.level_seeder import LevelSeeder +from modules.level.service.level_service import LevelService class LevelModule(ModuleABC): @@ -23,3 +24,4 @@ class LevelModule(ModuleABC): def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): services.add_transient(LevelSeeder) + services.add_transient(LevelService) diff --git a/kdb-bot/src/modules/level/service/__init__.py b/kdb-bot/src/modules/level/service/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/kdb-bot/src/modules/level/service/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/level/service/level_service.py b/kdb-bot/src/modules/level/service/level_service.py new file mode 100644 index 00000000..b3d2f664 --- /dev/null +++ b/kdb-bot/src/modules/level/service/level_service.py @@ -0,0 +1,53 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC +from cpl_discord.container import Guild, Role, Member +from cpl_discord.service import DiscordBotServiceABC + +from bot_data.model.level import Level +from bot_data.model.user import User +from bot_data.service.level_repository_service import LevelRepositoryService +from bot_data.service.user_repository_service import UserRepositoryService + + +class LevelService: + + def __init__( + self, + logger: LoggerABC, + db: DatabaseContextABC, + levels: LevelRepositoryService, + users: UserRepositoryService, + bot: DiscordBotServiceABC + ): + self._logger = logger + self._db = db + self._levels = levels + self._users = users + self._bot = bot + + def get_level(self, user: User) -> Level: + levels = self._levels.get_levels_by_server_id(user.server.server_id).order_by(lambda l: l.min_xp) + return levels.where(lambda l: l.min_xp > user.xp).first() + + async def set_level(self, user: User): + level_names = self._levels.get_levels_by_server_id(user.server.server_id).select(lambda l: l.name) + guild: Guild = self._bot.guilds.where(lambda g: g.id == user.server.discord_server_id).single() + level_role: Role = guild.roles.where(lambda r: r.name == self.get_level(user).name).single() + member: Member = guild.members.where(lambda m: m.id == user.discord_id).single() + for role in member.roles: + if role.name not in level_names.to_list(): + continue + + try: + self._logger.debug(__name__, f'Try to remove role {role.name} from {member.name}') + await member.remove_roles(role) + self._logger.info(__name__, f'Removed role {role.name} from {member.name}') + except Exception as e: + self._logger.error(__name__, f'Removing role {role.name} from {member.name} failed!', e) + + try: + self._logger.debug(__name__, f'Try to add role {level_role.name} to {member.name}') + await member.add_roles(level_role) + self._logger.info(__name__, f'Add role {level_role.name} to {member.name}') + except Exception as e: + self._logger.error(__name__, f'Adding role {level_role.name} to {member.name} failed!', e) From 164671a07c2391f2cc560e1ecc400ae44ef734d2 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 21:34:58 +0100 Subject: [PATCH 113/275] Added logic to handle level by xp #25 --- .../service/level_repository_service.py | 2 +- .../level/configuration/level_settings.py | 8 ++- kdb-bot/src/modules/level/default-level.json | 55 ++++++++++--------- kdb-bot/src/modules/level/events/__init__.py | 26 +++++++++ .../level/events/level_on_message_event.py | 21 +++++++ .../level_on_voice_state_update_event.py | 23 ++++++++ kdb-bot/src/modules/level/level_module.py | 6 ++ kdb-bot/src/modules/level/level_seeder.py | 12 ++-- .../modules/level/service/level_service.py | 19 ++++++- 9 files changed, 138 insertions(+), 34 deletions(-) create mode 100644 kdb-bot/src/modules/level/events/__init__.py create mode 100644 kdb-bot/src/modules/level/events/level_on_message_event.py create mode 100644 kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py diff --git a/kdb-bot/src/bot_data/service/level_repository_service.py b/kdb-bot/src/bot_data/service/level_repository_service.py index 37ee5952..3e41fc1b 100644 --- a/kdb-bot/src/bot_data/service/level_repository_service.py +++ b/kdb-bot/src/bot_data/service/level_repository_service.py @@ -63,7 +63,7 @@ class LevelRepositoryService(LevelRepositoryABC): def get_levels_by_server_id(self, server_id: int) -> List[Level]: levels = List(Level) self._logger.trace(__name__, f'Send SQL command: {Level.get_select_by_server_id_string(server_id)}') - results = self._context.select(Level.get_select_by_server_id_string(server_id))[0] + results = self._context.select(Level.get_select_by_server_id_string(server_id)) for result in results: self._logger.trace(__name__, f'Get level with id {result[0]}') diff --git a/kdb-bot/src/modules/level/configuration/level_settings.py b/kdb-bot/src/modules/level/configuration/level_settings.py index 95dda2c8..6f38e31b 100644 --- a/kdb-bot/src/modules/level/configuration/level_settings.py +++ b/kdb-bot/src/modules/level/configuration/level_settings.py @@ -13,14 +13,20 @@ class LevelSettings(ConfigurationModelABC): ConfigurationModelABC.__init__(self) self._levels = List(Level) + self._level_header = '' @property def levels(self) -> List[Level]: return self._levels + @property + def level_header(self) -> str: + return self._level_header + def from_dict(self, settings: dict): try: - for level in settings: + self._level_header = settings['LevelHeader'] + for level in settings['Levels']: self._levels.append(Level( level['Name'], level['Color'], diff --git a/kdb-bot/src/modules/level/default-level.json b/kdb-bot/src/modules/level/default-level.json index 04cb8745..451e7544 100644 --- a/kdb-bot/src/modules/level/default-level.json +++ b/kdb-bot/src/modules/level/default-level.json @@ -1,28 +1,31 @@ { - "Level": [ - { - "Name": "Newbie", - "Color": "0x1abc9c", - "MinXp": 0, - "Permissions": 968552209984 - }, - { - "Name": "Keks", - "Color": "0x2ecc71", - "MinXp": 100, - "Permissions": 1002928856640 - }, - { - "Name": "Doppelkeks", - "Color": "0x3498db", - "MinXp": 200, - "Permissions": 1071849660224 - }, - { - "Name": "Auror", - "Color": "0xf1c40f", - "MinXp": 300, - "Permissions": 1089042120513 - } - ] + "Level": { + "LevelHeader": "~~~ Level ~~~", + "Levels": [ + { + "Name": "Newbie", + "Color": "0x1abc9c", + "MinXp": 0, + "Permissions": 968552209984 + }, + { + "Name": "Keks", + "Color": "0x2ecc71", + "MinXp": 100, + "Permissions": 1002928856640 + }, + { + "Name": "Doppelkeks", + "Color": "0x3498db", + "MinXp": 200, + "Permissions": 1071849660224 + }, + { + "Name": "Auror", + "Color": "0xf1c40f", + "MinXp": 300, + "Permissions": 1089042120513 + } + ] + } } \ No newline at end of file diff --git a/kdb-bot/src/modules/level/events/__init__.py b/kdb-bot/src/modules/level/events/__init__.py new file mode 100644 index 00000000..85e09931 --- /dev/null +++ b/kdb-bot/src/modules/level/events/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'modules.base.events' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.3.dev70' + +from collections import namedtuple + + +# imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='dev70') diff --git a/kdb-bot/src/modules/level/events/level_on_message_event.py b/kdb-bot/src/modules/level/events/level_on_message_event.py new file mode 100644 index 00000000..cd69622e --- /dev/null +++ b/kdb-bot/src/modules/level/events/level_on_message_event.py @@ -0,0 +1,21 @@ +import discord +from cpl_discord.events import OnMessageABC + +from bot_core.logging.message_logger import MessageLogger +from modules.level.service.level_service import LevelService + + +class LevelOnMessageEvent(OnMessageABC): + + def __init__( + self, + logger: MessageLogger, + level: LevelService + ): + OnMessageABC.__init__(self) + self._logger = logger + self._level = level + + async def on_message(self, message: discord.Message): + self._logger.debug(__name__, f'Module {type(self)} started') + await self._level.check_level(message.author) diff --git a/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py b/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py new file mode 100644 index 00000000..c9127e03 --- /dev/null +++ b/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py @@ -0,0 +1,23 @@ +import discord +from cpl_core.logging import LoggerABC +from cpl_discord.events import OnVoiceStateUpdateABC + +from modules.level.service.level_service import LevelService + + +class LevelOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): + + def __init__( + self, + logger: LoggerABC, + level: LevelService + ): + OnVoiceStateUpdateABC.__init__(self) + self._logger = logger + self._level = level + + self._logger.info(__name__, f'Module {type(self)} loaded') + + async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): + self._logger.debug(__name__, f'Module {type(self)} started') + await self._level.check_level(member) diff --git a/kdb-bot/src/modules/level/level_module.py b/kdb-bot/src/modules/level/level_module.py index 760544ba..1943e73c 100644 --- a/kdb-bot/src/modules/level/level_module.py +++ b/kdb-bot/src/modules/level/level_module.py @@ -3,10 +3,13 @@ import os from cpl_core.configuration import ConfigurationABC from cpl_core.dependency_injection import ServiceCollectionABC from cpl_core.environment import ApplicationEnvironmentABC +from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from modules.level.events.level_on_message_event import LevelOnMessageEvent +from modules.level.events.level_on_voice_state_update_event import LevelOnVoiceStateUpdateEvent from modules.level.level_seeder import LevelSeeder from modules.level.service.level_service import LevelService @@ -25,3 +28,6 @@ class LevelModule(ModuleABC): def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): services.add_transient(LevelSeeder) services.add_transient(LevelService) + + self._dc.add_event(DiscordEventTypesEnum.on_message.value, LevelOnMessageEvent) + self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, LevelOnVoiceStateUpdateEvent) diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py index e7329126..377bfd2a 100644 --- a/kdb-bot/src/modules/level/level_seeder.py +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -18,14 +18,16 @@ class LevelSeeder(DataSeederABC): DataSeederABC.__init__(self) self._logger = logger - self._default_levels = levels.levels.order_by_descending(lambda l: l.min_xp) self._levels = level_repo self._servers = servers self._bot = bot + self._level_header = levels.level_header + self._default_levels = levels.levels.order_by_descending(lambda l: l.min_xp) + async def _create_level(self, level: Level, guild: Guild, server: Server): try: - if guild.roles.where(lambda r: r.name == level.name).count() > 0: + if guild.roles.where(lambda r: r.name == level.name).first_or_default() is not None: return await guild.create_role(name=level.name, colour=Colour(int(level.color, 16)), hoist=False, mentionable=True, permissions=Permissions(level.permissions)) @@ -39,8 +41,8 @@ class LevelSeeder(DataSeederABC): # create levels for guild in self._bot.guilds: created_default = False - if guild.roles.where(lambda r: r.name == '___ Level ___').count() == 0: - await guild.create_role(name='___ Level ___') + if guild.roles.where(lambda r: r.name == self._level_header).first_or_default() is None: + await guild.create_role(name=self._level_header) server = self._servers.find_server_by_discord_id(guild.id) if server is None: @@ -64,7 +66,7 @@ class LevelSeeder(DataSeederABC): if created_default: continue - position_above_levels = guild.roles.where(lambda r: r.name == '~~~ Level ~~~').single().position + position_above_levels = guild.roles.where(lambda r: r.name == self._level_header).single().position for role in guild.roles.order_by_descending(lambda r: r.position): if levels.where(lambda l: l.name == role.name).count() == 0: continue diff --git a/kdb-bot/src/modules/level/service/level_service.py b/kdb-bot/src/modules/level/service/level_service.py index b3d2f664..95fbcae6 100644 --- a/kdb-bot/src/modules/level/service/level_service.py +++ b/kdb-bot/src/modules/level/service/level_service.py @@ -1,3 +1,4 @@ +import discord from cpl_core.database.context import DatabaseContextABC from cpl_core.logging import LoggerABC from cpl_discord.container import Guild, Role, Member @@ -6,6 +7,7 @@ from cpl_discord.service import DiscordBotServiceABC from bot_data.model.level import Level from bot_data.model.user import User from bot_data.service.level_repository_service import LevelRepositoryService +from bot_data.service.server_repository_service import ServerRepositoryService from bot_data.service.user_repository_service import UserRepositoryService @@ -17,17 +19,19 @@ class LevelService: db: DatabaseContextABC, levels: LevelRepositoryService, users: UserRepositoryService, + servers: ServerRepositoryService, bot: DiscordBotServiceABC ): self._logger = logger self._db = db self._levels = levels self._users = users + self._servers = servers self._bot = bot def get_level(self, user: User) -> Level: levels = self._levels.get_levels_by_server_id(user.server.server_id).order_by(lambda l: l.min_xp) - return levels.where(lambda l: l.min_xp > user.xp).first() + return levels.where(lambda l: user.xp >= l.min_xp).first() async def set_level(self, user: User): level_names = self._levels.get_levels_by_server_id(user.server.server_id).select(lambda l: l.name) @@ -45,9 +49,22 @@ class LevelService: except Exception as e: self._logger.error(__name__, f'Removing role {role.name} from {member.name} failed!', e) + if level_role in member.roles: + return try: self._logger.debug(__name__, f'Try to add role {level_role.name} to {member.name}') await member.add_roles(level_role) self._logger.info(__name__, f'Add role {level_role.name} to {member.name}') except Exception as e: self._logger.error(__name__, f'Adding role {level_role.name} to {member.name} failed!', e) + + async def check_level(self, member: discord.Member): + if member.bot: + return + + server = self._servers.get_server_by_discord_id(member.guild.id) + user = self._users.find_user_by_discord_id_and_server_id(member.id, server.server_id) + if user is None: + self._logger.warn(__name__, f'User not found {member.guild.name}@{member.name}') + + await self.set_level(user) From 6e39154c75a908e285dcd306d03308c57f3e52e4 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 21:54:21 +0100 Subject: [PATCH 114/275] Fixed level assignment #25 --- kdb-bot/src/modules/level/service/level_service.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kdb-bot/src/modules/level/service/level_service.py b/kdb-bot/src/modules/level/service/level_service.py index 95fbcae6..292d548a 100644 --- a/kdb-bot/src/modules/level/service/level_service.py +++ b/kdb-bot/src/modules/level/service/level_service.py @@ -1,3 +1,5 @@ +import asyncio + import discord from cpl_core.database.context import DatabaseContextABC from cpl_core.logging import LoggerABC @@ -31,12 +33,11 @@ class LevelService: def get_level(self, user: User) -> Level: levels = self._levels.get_levels_by_server_id(user.server.server_id).order_by(lambda l: l.min_xp) - return levels.where(lambda l: user.xp >= l.min_xp).first() + return levels.where(lambda l: user.xp >= l.min_xp).last() async def set_level(self, user: User): level_names = self._levels.get_levels_by_server_id(user.server.server_id).select(lambda l: l.name) guild: Guild = self._bot.guilds.where(lambda g: g.id == user.server.discord_server_id).single() - level_role: Role = guild.roles.where(lambda r: r.name == self.get_level(user).name).single() member: Member = guild.members.where(lambda m: m.id == user.discord_id).single() for role in member.roles: if role.name not in level_names.to_list(): @@ -49,6 +50,7 @@ class LevelService: except Exception as e: self._logger.error(__name__, f'Removing role {role.name} from {member.name} failed!', e) + level_role: Role = guild.roles.where(lambda r: r.name == self.get_level(user).name).single() if level_role in member.roles: return try: From 6640d70440e3dc759f1f8745c427ef5b15a630a8 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 22:17:17 +0100 Subject: [PATCH 115/275] Added logic to send message after level up #25 --- .../bot/config/appsettings.edrafts-pc.json | 7 +++- kdb-bot/src/bot/startup_settings_extension.py | 2 + .../src/bot_core/service/message_service.py | 9 ++++- .../configuration/default_level_settings.py | 39 +++++++++++++++++++ .../configuration/level_server_settings.py | 29 ++++++++++++++ .../level/configuration/level_settings.py | 29 ++++++-------- kdb-bot/src/modules/level/default-level.json | 2 +- kdb-bot/src/modules/level/level_seeder.py | 4 +- .../modules/level/service/level_service.py | 25 ++++++++++-- 9 files changed, 119 insertions(+), 27 deletions(-) create mode 100644 kdb-bot/src/modules/level/configuration/default_level_settings.py create mode 100644 kdb-bot/src/modules/level/configuration/level_server_settings.py diff --git a/kdb-bot/src/bot/config/appsettings.edrafts-pc.json b/kdb-bot/src/bot/config/appsettings.edrafts-pc.json index 9deca017..eca8346d 100644 --- a/kdb-bot/src/bot/config/appsettings.edrafts-pc.json +++ b/kdb-bot/src/bot/config/appsettings.edrafts-pc.json @@ -76,7 +76,12 @@ }, "BootLog": { "910199451145076828": { - "LoginMessageChannelId": "910199452915093588" + "LoginMessageChannelId": 910199452667637892 + } + }, + "Level": { + "910199451145076828": { + "ChangedLevelNotificationChannelId": 910199452915093588 } }, "Permission": { diff --git a/kdb-bot/src/bot/startup_settings_extension.py b/kdb-bot/src/bot/startup_settings_extension.py index 4dcee8bf..655ccd7b 100644 --- a/kdb-bot/src/bot/startup_settings_extension.py +++ b/kdb-bot/src/bot/startup_settings_extension.py @@ -11,6 +11,7 @@ from bot_core.configuration.bot_logging_settings import BotLoggingSettings from bot_core.configuration.bot_settings import BotSettings from modules.base.configuration.base_settings import BaseSettings from modules.boot_log.configuration.boot_log_settings import BootLogSettings +from modules.level.configuration.level_settings import LevelSettings from modules.permission.configuration.permission_settings import PermissionSettings @@ -35,6 +36,7 @@ class StartupSettingsExtension(StartupExtensionABC): self._configure_settings_with_sub_settings(configuration, BotSettings, lambda x: x.servers, lambda x: x.id) self._configure_settings_with_sub_settings(configuration, BaseSettings, lambda x: x.servers, lambda x: x.id) self._configure_settings_with_sub_settings(configuration, BootLogSettings, lambda x: x.servers, lambda x: x.id) + self._configure_settings_with_sub_settings(configuration, LevelSettings, lambda x: x.servers, lambda x: x.id) self._configure_settings_with_sub_settings(configuration, PermissionSettings, lambda x: x.servers, lambda x: x.id) self._configure_settings_with_sub_settings(configuration, BotLoggingSettings, lambda x: x.files, lambda x: x.key) diff --git a/kdb-bot/src/bot_core/service/message_service.py b/kdb-bot/src/bot_core/service/message_service.py index 7094236d..6a43f32c 100644 --- a/kdb-bot/src/bot_core/service/message_service.py +++ b/kdb-bot/src/bot_core/service/message_service.py @@ -49,7 +49,7 @@ class MessageService(MessageServiceABC): self._db.save_changes() self._logger.info(__name__, f'Deleted message {message}') - async def send_channel_message(self, channel: discord.TextChannel, message: Union[str, discord.Embed], without_tracking=False): + async def send_channel_message(self, channel: discord.TextChannel, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=False): self._logger.debug(__name__, f'Try to send message\n\t{message}\n\tto: {channel}') msg = None try: @@ -64,6 +64,13 @@ class MessageService(MessageServiceABC): if not without_tracking: self._clients.append_sent_message_count(self._bot.user.id, channel.guild.id, 1) self._db.save_changes() + + if wait_before_delete is not None: + await asyncio.sleep(wait_before_delete) + + if is_persistent: + return + await self.delete_message(msg, without_tracking) async def send_dm_message(self, message: Union[str, discord.Embed], receiver: Union[discord.User, discord.Member], without_tracking=False): diff --git a/kdb-bot/src/modules/level/configuration/default_level_settings.py b/kdb-bot/src/modules/level/configuration/default_level_settings.py new file mode 100644 index 00000000..3687ab83 --- /dev/null +++ b/kdb-bot/src/modules/level/configuration/default_level_settings.py @@ -0,0 +1,39 @@ +import traceback + +from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC +from cpl_core.console import Console +from cpl_query.extension import List + +from bot_data.model.level import Level + + +class DefaultLevelSettings(ConfigurationModelABC): + + def __init__(self): + ConfigurationModelABC.__init__(self) + + self._levels = List(Level) + self._level_header = '' + + @property + def levels(self) -> List[Level]: + return self._levels + + @property + def level_header(self) -> str: + return self._level_header + + def from_dict(self, settings: dict): + try: + self._level_header = settings['LevelHeader'] + for level in settings['Levels']: + self._levels.append(Level( + level['Name'], + level['Color'], + int(level['MinXp']), + int(level['Permissions']), + None + )) + except Exception as e: + Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') + Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') diff --git a/kdb-bot/src/modules/level/configuration/level_server_settings.py b/kdb-bot/src/modules/level/configuration/level_server_settings.py new file mode 100644 index 00000000..85985cff --- /dev/null +++ b/kdb-bot/src/modules/level/configuration/level_server_settings.py @@ -0,0 +1,29 @@ +import traceback + +from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC +from cpl_core.console import Console + + +class LevelServerSettings(ConfigurationModelABC): + + def __init__(self): + ConfigurationModelABC.__init__(self) + + self._id: int = 0 + self._changed_level_notification_channel = 0 + + @property + def id(self) -> int: + return self._id + + @property + def changed_level_notification_channel(self) -> int: + return self._changed_level_notification_channel + + def from_dict(self, settings: dict): + try: + self._id = int(settings['Id']) + self._changed_level_notification_channel = int(settings['ChangedLevelNotificationChannelId']) + except Exception as e: + Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') + Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') diff --git a/kdb-bot/src/modules/level/configuration/level_settings.py b/kdb-bot/src/modules/level/configuration/level_settings.py index 6f38e31b..7962941c 100644 --- a/kdb-bot/src/modules/level/configuration/level_settings.py +++ b/kdb-bot/src/modules/level/configuration/level_settings.py @@ -4,7 +4,7 @@ from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC from cpl_core.console import Console from cpl_query.extension import List -from bot_data.model.level import Level +from modules.level.configuration.level_server_settings import LevelServerSettings class LevelSettings(ConfigurationModelABC): @@ -12,28 +12,21 @@ class LevelSettings(ConfigurationModelABC): def __init__(self): ConfigurationModelABC.__init__(self) - self._levels = List(Level) - self._level_header = '' + self._servers: List[LevelServerSettings] = List() @property - def levels(self) -> List[Level]: - return self._levels - - @property - def level_header(self) -> str: - return self._level_header + def servers(self) -> List[LevelServerSettings]: + return self._servers def from_dict(self, settings: dict): try: - self._level_header = settings['LevelHeader'] - for level in settings['Levels']: - self._levels.append(Level( - level['Name'], - level['Color'], - int(level['MinXp']), - int(level['Permissions']), - None - )) + servers = List(LevelServerSettings) + for s in settings: + st = LevelServerSettings() + settings[s]['Id'] = s + st.from_dict(settings[s]) + servers.append(st) + self._servers = servers except Exception as e: Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') diff --git a/kdb-bot/src/modules/level/default-level.json b/kdb-bot/src/modules/level/default-level.json index 451e7544..047b811a 100644 --- a/kdb-bot/src/modules/level/default-level.json +++ b/kdb-bot/src/modules/level/default-level.json @@ -1,5 +1,5 @@ { - "Level": { + "DefaultLevel": { "LevelHeader": "~~~ Level ~~~", "Levels": [ { diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py index 377bfd2a..9a1148bb 100644 --- a/kdb-bot/src/modules/level/level_seeder.py +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -9,12 +9,12 @@ from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.model.level import Level from bot_data.model.server import Server from bot_data.service.level_repository_service import LevelRepositoryService -from modules.level.configuration.level_settings import LevelSettings +from modules.level.configuration.default_level_settings import DefaultLevelSettings class LevelSeeder(DataSeederABC): - def __init__(self, logger: DatabaseLogger, levels: LevelSettings, level_repo: LevelRepositoryService, servers: ServerRepositoryABC, bot: DiscordBotServiceABC): + def __init__(self, logger: DatabaseLogger, levels: DefaultLevelSettings, level_repo: LevelRepositoryService, servers: ServerRepositoryABC, bot: DiscordBotServiceABC): DataSeederABC.__init__(self) self._logger = logger diff --git a/kdb-bot/src/modules/level/service/level_service.py b/kdb-bot/src/modules/level/service/level_service.py index 292d548a..6385b9ae 100644 --- a/kdb-bot/src/modules/level/service/level_service.py +++ b/kdb-bot/src/modules/level/service/level_service.py @@ -1,35 +1,42 @@ import asyncio import discord +from cpl_core.configuration import ConfigurationABC from cpl_core.database.context import DatabaseContextABC from cpl_core.logging import LoggerABC from cpl_discord.container import Guild, Role, Member from cpl_discord.service import DiscordBotServiceABC +from bot_core.service.message_service import MessageService from bot_data.model.level import Level from bot_data.model.user import User from bot_data.service.level_repository_service import LevelRepositoryService from bot_data.service.server_repository_service import ServerRepositoryService from bot_data.service.user_repository_service import UserRepositoryService +from modules.level.configuration.level_server_settings import LevelServerSettings class LevelService: def __init__( self, + config: ConfigurationABC, logger: LoggerABC, db: DatabaseContextABC, levels: LevelRepositoryService, users: UserRepositoryService, servers: ServerRepositoryService, - bot: DiscordBotServiceABC + bot: DiscordBotServiceABC, + message_service: MessageService ): + self._config = config self._logger = logger self._db = db self._levels = levels self._users = users self._servers = servers self._bot = bot + self._message_service = message_service def get_level(self, user: User) -> Level: levels = self._levels.get_levels_by_server_id(user.server.server_id).order_by(lambda l: l.min_xp) @@ -39,6 +46,12 @@ class LevelService: level_names = self._levels.get_levels_by_server_id(user.server.server_id).select(lambda l: l.name) guild: Guild = self._bot.guilds.where(lambda g: g.id == user.server.discord_server_id).single() member: Member = guild.members.where(lambda m: m.id == user.discord_id).single() + + level = self.get_level(user) + level_role: Role = guild.roles.where(lambda r: r.name == level.name).single() + if level_role in member.roles: + return + for role in member.roles: if role.name not in level_names.to_list(): continue @@ -50,9 +63,6 @@ class LevelService: except Exception as e: self._logger.error(__name__, f'Removing role {role.name} from {member.name} failed!', e) - level_role: Role = guild.roles.where(lambda r: r.name == self.get_level(user).name).single() - if level_role in member.roles: - return try: self._logger.debug(__name__, f'Try to add role {level_role.name} to {member.name}') await member.add_roles(level_role) @@ -60,6 +70,13 @@ class LevelService: except Exception as e: self._logger.error(__name__, f'Adding role {level_role.name} to {member.name} failed!', e) + level_settings: LevelServerSettings = self._config.get_configuration(f'LevelServerSettings_{guild.id}') + await self._message_service.send_channel_message( + self._bot.get_channel(level_settings.changed_level_notification_channel), + f'<@{member.id}> ist nun Level {level.name}', + is_persistent=True + ) + async def check_level(self, member: discord.Member): if member.bot: return From 23fde793db28a4d8aef3c413bc70de91c8de4ad0 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 22:19:41 +0100 Subject: [PATCH 116/275] Fixed my config #25 --- kdb-bot/src/bot/config/appsettings.edrafts-pc.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kdb-bot/src/bot/config/appsettings.edrafts-pc.json b/kdb-bot/src/bot/config/appsettings.edrafts-pc.json index eca8346d..e912b148 100644 --- a/kdb-bot/src/bot/config/appsettings.edrafts-pc.json +++ b/kdb-bot/src/bot/config/appsettings.edrafts-pc.json @@ -76,12 +76,12 @@ }, "BootLog": { "910199451145076828": { - "LoginMessageChannelId": 910199452667637892 + "LoginMessageChannelId": 910199452915093588 } }, "Level": { "910199451145076828": { - "ChangedLevelNotificationChannelId": 910199452915093588 + "ChangedLevelNotificationChannelId": 910199452667637892 } }, "Permission": { From dc90258a282bceb1ef3567e5a52ce0129fa18d11 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 22:30:57 +0100 Subject: [PATCH 117/275] "Repaired" workspace file #25 --- kdb-bot/cpl-workspace.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kdb-bot/cpl-workspace.json b/kdb-bot/cpl-workspace.json index 37a8b6c6..7af45bfe 100644 --- a/kdb-bot/cpl-workspace.json +++ b/kdb-bot/cpl-workspace.json @@ -10,27 +10,33 @@ "base": "src/modules/base/base.json", "boot-log": "src/modules/boot_log/boot-log.json", "database": "src/modules/database/database.json", + "level": "src/modules/level/level.json", "moderator": "src/modules/moderator/moderator.json", "permission": "src/modules/permission/permission.json", "bot-api": "src/bot_api/bot-api.json", "get-version": "tools/get_version/get-version.json", "post-build": "tools/post_build/post-build.json", - "set-version": "tools/set_version/set-version.json", - "level": "src/modules/level/level.json" + "set-version": "tools/set_version/set-version.json" }, "Scripts": { "sv": "cpl set-version", "set-version": "cpl run set-version $ARGS; echo '';", + "gv": "cpl get-version", "get-version": "export VERSION=$(cpl run get-version); echo $VERSION;", + "pre-build": "cpl set-version $ARGS", "post-build": "cpl run post-build", + "pre-prod": "cpl build", "prod": "export KDB_ENVIRONMENT=production; export KDB_NAME=KDB-Prod; cpl start;", + "pre-stage": "cpl build", "stage": "export KDB_ENVIRONMENT=staging; export KDB_NAME=KDB-Stage; cpl start;", + "pre-dev": "cpl build", "dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;", + "docker-build": "cpl b; docker-compose down; docker build -t kdb-bot/kdb-bot:$(cpl gv) .", "docker-compose": "docker-compose up -d", "docker": "cpl docker-build; cpl docker-compose;" From 1b587b049a34eb992347a97b181ae5eccd9c9016 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 22:40:30 +0100 Subject: [PATCH 118/275] Build files #25 --- kdb-bot/src/bot/__init__.py | 4 +-- kdb-bot/src/bot/bot.json | 3 ++- kdb-bot/src/bot_api/__init__.py | 4 +-- kdb-bot/src/bot_api/abc/__init__.py | 4 +-- kdb-bot/src/bot_api/configuration/__init__.py | 4 +-- kdb-bot/src/bot_api/controller/__init__.py | 4 +-- .../bot_api/controller/discord/__init__.py | 4 +-- kdb-bot/src/bot_api/event/__init__.py | 4 +-- kdb-bot/src/bot_api/exception/__init__.py | 4 +-- kdb-bot/src/bot_api/filter/__init__.py | 4 +-- .../src/bot_api/filter/discord/__init__.py | 4 +-- kdb-bot/src/bot_api/logging/__init__.py | 4 +-- kdb-bot/src/bot_api/model/__init__.py | 4 +-- kdb-bot/src/bot_api/model/discord/__init__.py | 4 +-- kdb-bot/src/bot_api/route/__init__.py | 4 +-- kdb-bot/src/bot_api/service/__init__.py | 4 +-- kdb-bot/src/bot_api/transformer/__init__.py | 4 +-- kdb-bot/src/bot_core/__init__.py | 4 +-- kdb-bot/src/bot_core/abc/__init__.py | 4 +-- .../src/bot_core/configuration/__init__.py | 4 +-- .../src/bot_core/core_extension/__init__.py | 4 +-- kdb-bot/src/bot_core/events/__init__.py | 4 +-- kdb-bot/src/bot_core/helper/__init__.py | 4 +-- kdb-bot/src/bot_core/logging/__init__.py | 4 +-- kdb-bot/src/bot_core/pipes/__init__.py | 4 +-- kdb-bot/src/bot_core/service/__init__.py | 4 +-- kdb-bot/src/bot_data/__init__.py | 4 +-- kdb-bot/src/bot_data/abc/__init__.py | 4 +-- kdb-bot/src/bot_data/migration/__init__.py | 4 +-- kdb-bot/src/bot_data/model/__init__.py | 4 +-- kdb-bot/src/bot_data/service/__init__.py | 4 +-- kdb-bot/src/modules/admin/__init__.py | 4 +-- kdb-bot/src/modules/admin/command/__init__.py | 4 +-- kdb-bot/src/modules/auto_role/__init__.py | 4 +-- .../src/modules/auto_role/command/__init__.py | 4 +-- .../src/modules/auto_role/events/__init__.py | 4 +-- .../src/modules/auto_role/helper/__init__.py | 4 +-- kdb-bot/src/modules/base/__init__.py | 4 +-- kdb-bot/src/modules/base/abc/__init__.py | 4 +-- kdb-bot/src/modules/base/command/__init__.py | 4 +-- .../modules/base/configuration/__init__.py | 4 +-- kdb-bot/src/modules/base/events/__init__.py | 4 +-- kdb-bot/src/modules/base/service/__init__.py | 4 +-- kdb-bot/src/modules/boot_log/__init__.py | 4 +-- .../boot_log/configuration/__init__.py | 4 +-- kdb-bot/src/modules/database/__init__.py | 4 +-- kdb-bot/src/modules/level/__init__.py | 25 +++++++++++++++++++ .../modules/level/configuration/__init__.py | 25 +++++++++++++++++++ kdb-bot/src/modules/level/events/__init__.py | 6 ++--- kdb-bot/src/modules/level/service/__init__.py | 25 +++++++++++++++++++ kdb-bot/src/modules/moderator/__init__.py | 4 +-- .../src/modules/moderator/command/__init__.py | 4 +-- kdb-bot/src/modules/permission/__init__.py | 4 +-- .../src/modules/permission/abc/__init__.py | 4 +-- .../permission/configuration/__init__.py | 4 +-- .../src/modules/permission/events/__init__.py | 4 +-- .../modules/permission/service/__init__.py | 4 +-- 57 files changed, 184 insertions(+), 108 deletions(-) diff --git a/kdb-bot/src/bot/__init__.py b/kdb-bot/src/bot/__init__.py index 69dfae3a..9184f6b9 100644 --- a/kdb-bot/src/bot/__init__.py +++ b/kdb-bot/src/bot/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot/bot.json b/kdb-bot/src/bot/bot.json index 36270e64..db8ef6fe 100644 --- a/kdb-bot/src/bot/bot.json +++ b/kdb-bot/src/bot/bot.json @@ -4,7 +4,7 @@ "Version": { "Major": "0", "Minor": "3", - "Micro": "dev70" + "Micro": "dev25" }, "Author": "Sven Heidemann", "AuthorEmail": "sven.heidemann@sh-edraft.de", @@ -60,6 +60,7 @@ "../modules/base/base.json", "../modules/boot_log/boot-log.json", "../modules/database/database.json", + "../modules/level/level.json", "../modules/moderator/moderator.json", "../modules/permission/permission.json" ] diff --git a/kdb-bot/src/bot_api/__init__.py b/kdb-bot/src/bot_api/__init__.py index 3b884c17..c2ec271a 100644 --- a/kdb-bot/src/bot_api/__init__.py +++ b/kdb-bot/src/bot_api/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/abc/__init__.py b/kdb-bot/src/bot_api/abc/__init__.py index 93c7c132..20368eb6 100644 --- a/kdb-bot/src/bot_api/abc/__init__.py +++ b/kdb-bot/src/bot_api/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.abc' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/configuration/__init__.py b/kdb-bot/src/bot_api/configuration/__init__.py index e6ba2a1b..ab482a28 100644 --- a/kdb-bot/src/bot_api/configuration/__init__.py +++ b/kdb-bot/src/bot_api/configuration/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.configuration' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/controller/__init__.py b/kdb-bot/src/bot_api/controller/__init__.py index 3a46a81c..122f580c 100644 --- a/kdb-bot/src/bot_api/controller/__init__.py +++ b/kdb-bot/src/bot_api/controller/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.controller' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/controller/discord/__init__.py b/kdb-bot/src/bot_api/controller/discord/__init__.py index a23ef460..975b9cff 100644 --- a/kdb-bot/src/bot_api/controller/discord/__init__.py +++ b/kdb-bot/src/bot_api/controller/discord/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.controller.discord' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/event/__init__.py b/kdb-bot/src/bot_api/event/__init__.py index 7a717ae7..23b33965 100644 --- a/kdb-bot/src/bot_api/event/__init__.py +++ b/kdb-bot/src/bot_api/event/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.event' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/exception/__init__.py b/kdb-bot/src/bot_api/exception/__init__.py index d23b0c22..a8c5329d 100644 --- a/kdb-bot/src/bot_api/exception/__init__.py +++ b/kdb-bot/src/bot_api/exception/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.exception' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/filter/__init__.py b/kdb-bot/src/bot_api/filter/__init__.py index 800e7300..8dc3d974 100644 --- a/kdb-bot/src/bot_api/filter/__init__.py +++ b/kdb-bot/src/bot_api/filter/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.filter' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/filter/discord/__init__.py b/kdb-bot/src/bot_api/filter/discord/__init__.py index cb6b161f..d228164f 100644 --- a/kdb-bot/src/bot_api/filter/discord/__init__.py +++ b/kdb-bot/src/bot_api/filter/discord/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.filter.discord' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/logging/__init__.py b/kdb-bot/src/bot_api/logging/__init__.py index 7877e2bb..24cc8ae1 100644 --- a/kdb-bot/src/bot_api/logging/__init__.py +++ b/kdb-bot/src/bot_api/logging/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.logging' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/model/__init__.py b/kdb-bot/src/bot_api/model/__init__.py index bf076571..be2392ef 100644 --- a/kdb-bot/src/bot_api/model/__init__.py +++ b/kdb-bot/src/bot_api/model/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.model' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/model/discord/__init__.py b/kdb-bot/src/bot_api/model/discord/__init__.py index 9e0ec39b..667ceb2a 100644 --- a/kdb-bot/src/bot_api/model/discord/__init__.py +++ b/kdb-bot/src/bot_api/model/discord/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.model.discord' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/route/__init__.py b/kdb-bot/src/bot_api/route/__init__.py index f781f96d..230ae05d 100644 --- a/kdb-bot/src/bot_api/route/__init__.py +++ b/kdb-bot/src/bot_api/route/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.route' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/service/__init__.py b/kdb-bot/src/bot_api/service/__init__.py index ae381d3d..d6b86d11 100644 --- a/kdb-bot/src/bot_api/service/__init__.py +++ b/kdb-bot/src/bot_api/service/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_api/transformer/__init__.py b/kdb-bot/src/bot_api/transformer/__init__.py index 0dc9db2a..50cc9ad5 100644 --- a/kdb-bot/src/bot_api/transformer/__init__.py +++ b/kdb-bot/src/bot_api/transformer/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_api.transformer' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_core/__init__.py b/kdb-bot/src/bot_core/__init__.py index 178a3f4e..bc622310 100644 --- a/kdb-bot/src/bot_core/__init__.py +++ b/kdb-bot/src/bot_core/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_core/abc/__init__.py b/kdb-bot/src/bot_core/abc/__init__.py index a388e22a..4056210a 100644 --- a/kdb-bot/src/bot_core/abc/__init__.py +++ b/kdb-bot/src/bot_core/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.abc' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_core/configuration/__init__.py b/kdb-bot/src/bot_core/configuration/__init__.py index 6dbe1791..8e677c73 100644 --- a/kdb-bot/src/bot_core/configuration/__init__.py +++ b/kdb-bot/src/bot_core/configuration/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.configuration' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_core/core_extension/__init__.py b/kdb-bot/src/bot_core/core_extension/__init__.py index 09f8f237..a107f3d8 100644 --- a/kdb-bot/src/bot_core/core_extension/__init__.py +++ b/kdb-bot/src/bot_core/core_extension/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.core_extension' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_core/events/__init__.py b/kdb-bot/src/bot_core/events/__init__.py index 885b39c3..b73ac3fb 100644 --- a/kdb-bot/src/bot_core/events/__init__.py +++ b/kdb-bot/src/bot_core/events/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.events' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_core/helper/__init__.py b/kdb-bot/src/bot_core/helper/__init__.py index 1b4972d0..0f0edc20 100644 --- a/kdb-bot/src/bot_core/helper/__init__.py +++ b/kdb-bot/src/bot_core/helper/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.helper' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_core/logging/__init__.py b/kdb-bot/src/bot_core/logging/__init__.py index eb87740c..ba6fe7ad 100644 --- a/kdb-bot/src/bot_core/logging/__init__.py +++ b/kdb-bot/src/bot_core/logging/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.logging' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_core/pipes/__init__.py b/kdb-bot/src/bot_core/pipes/__init__.py index 1cad1f88..85c44be7 100644 --- a/kdb-bot/src/bot_core/pipes/__init__.py +++ b/kdb-bot/src/bot_core/pipes/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.pipes' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_core/service/__init__.py b/kdb-bot/src/bot_core/service/__init__.py index a5c0718e..1266f4f1 100644 --- a/kdb-bot/src/bot_core/service/__init__.py +++ b/kdb-bot/src/bot_core/service/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_core.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_data/__init__.py b/kdb-bot/src/bot_data/__init__.py index cf89c13c..5a1acbdf 100644 --- a/kdb-bot/src/bot_data/__init__.py +++ b/kdb-bot/src/bot_data/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_data' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_data/abc/__init__.py b/kdb-bot/src/bot_data/abc/__init__.py index 1e3d6dcf..7209be45 100644 --- a/kdb-bot/src/bot_data/abc/__init__.py +++ b/kdb-bot/src/bot_data/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_data.abc' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_data/migration/__init__.py b/kdb-bot/src/bot_data/migration/__init__.py index b716abf9..ed8bf58f 100644 --- a/kdb-bot/src/bot_data/migration/__init__.py +++ b/kdb-bot/src/bot_data/migration/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_data.migration' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_data/model/__init__.py b/kdb-bot/src/bot_data/model/__init__.py index 562dec84..0c33f8c7 100644 --- a/kdb-bot/src/bot_data/model/__init__.py +++ b/kdb-bot/src/bot_data/model/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_data.model' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/bot_data/service/__init__.py b/kdb-bot/src/bot_data/service/__init__.py index 3ff47a8a..c2b4323a 100644 --- a/kdb-bot/src/bot_data/service/__init__.py +++ b/kdb-bot/src/bot_data/service/__init__.py @@ -15,7 +15,7 @@ __title__ = 'bot_data.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/admin/__init__.py b/kdb-bot/src/modules/admin/__init__.py index 0b4143b8..afcd237e 100644 --- a/kdb-bot/src/modules/admin/__init__.py +++ b/kdb-bot/src/modules/admin/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.admin' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/admin/command/__init__.py b/kdb-bot/src/modules/admin/command/__init__.py index 560c44e5..ed0cff3b 100644 --- a/kdb-bot/src/modules/admin/command/__init__.py +++ b/kdb-bot/src/modules/admin/command/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.admin.command' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/auto_role/__init__.py b/kdb-bot/src/modules/auto_role/__init__.py index c98076f2..4a853323 100644 --- a/kdb-bot/src/modules/auto_role/__init__.py +++ b/kdb-bot/src/modules/auto_role/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.auto_role' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/auto_role/command/__init__.py b/kdb-bot/src/modules/auto_role/command/__init__.py index 893b4fd8..86a6041d 100644 --- a/kdb-bot/src/modules/auto_role/command/__init__.py +++ b/kdb-bot/src/modules/auto_role/command/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.auto_role.command' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/auto_role/events/__init__.py b/kdb-bot/src/modules/auto_role/events/__init__.py index b3c13b1c..29463096 100644 --- a/kdb-bot/src/modules/auto_role/events/__init__.py +++ b/kdb-bot/src/modules/auto_role/events/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.auto_role.events' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/auto_role/helper/__init__.py b/kdb-bot/src/modules/auto_role/helper/__init__.py index dde66d85..a19a540f 100644 --- a/kdb-bot/src/modules/auto_role/helper/__init__.py +++ b/kdb-bot/src/modules/auto_role/helper/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.auto_role.helper' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/base/__init__.py b/kdb-bot/src/modules/base/__init__.py index 9013b6d0..624c476e 100644 --- a/kdb-bot/src/modules/base/__init__.py +++ b/kdb-bot/src/modules/base/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.base' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/base/abc/__init__.py b/kdb-bot/src/modules/base/abc/__init__.py index 4f366eaa..ccc3e98a 100644 --- a/kdb-bot/src/modules/base/abc/__init__.py +++ b/kdb-bot/src/modules/base/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.base.abc' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/base/command/__init__.py b/kdb-bot/src/modules/base/command/__init__.py index a1c94297..c9fccb7a 100644 --- a/kdb-bot/src/modules/base/command/__init__.py +++ b/kdb-bot/src/modules/base/command/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.base.command' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/base/configuration/__init__.py b/kdb-bot/src/modules/base/configuration/__init__.py index 1c3feda7..5dfdba22 100644 --- a/kdb-bot/src/modules/base/configuration/__init__.py +++ b/kdb-bot/src/modules/base/configuration/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.base.configuration' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/base/events/__init__.py b/kdb-bot/src/modules/base/events/__init__.py index 85e09931..8b048069 100644 --- a/kdb-bot/src/modules/base/events/__init__.py +++ b/kdb-bot/src/modules/base/events/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.base.events' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/base/service/__init__.py b/kdb-bot/src/modules/base/service/__init__.py index 26d484da..4362aaf1 100644 --- a/kdb-bot/src/modules/base/service/__init__.py +++ b/kdb-bot/src/modules/base/service/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.base.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/boot_log/__init__.py b/kdb-bot/src/modules/boot_log/__init__.py index a8e3acd4..afe1ddbd 100644 --- a/kdb-bot/src/modules/boot_log/__init__.py +++ b/kdb-bot/src/modules/boot_log/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.boot_log' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/boot_log/configuration/__init__.py b/kdb-bot/src/modules/boot_log/configuration/__init__.py index a7ee6a4e..1eee9aba 100644 --- a/kdb-bot/src/modules/boot_log/configuration/__init__.py +++ b/kdb-bot/src/modules/boot_log/configuration/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.boot_log.configuration' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/database/__init__.py b/kdb-bot/src/modules/database/__init__.py index 122dd6eb..a1425bbb 100644 --- a/kdb-bot/src/modules/database/__init__.py +++ b/kdb-bot/src/modules/database/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.database' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/level/__init__.py b/kdb-bot/src/modules/level/__init__.py index ad5eca30..d1d67ee3 100644 --- a/kdb-bot/src/modules/level/__init__.py +++ b/kdb-bot/src/modules/level/__init__.py @@ -1 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'modules.level' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.3.dev25' + +from collections import namedtuple + + # imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/level/configuration/__init__.py b/kdb-bot/src/modules/level/configuration/__init__.py index 425ab6c1..a30d06e0 100644 --- a/kdb-bot/src/modules/level/configuration/__init__.py +++ b/kdb-bot/src/modules/level/configuration/__init__.py @@ -1 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'modules.level.configuration' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.3.dev25' + +from collections import namedtuple + + # imports + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/level/events/__init__.py b/kdb-bot/src/modules/level/events/__init__.py index 85e09931..5925acf8 100644 --- a/kdb-bot/src/modules/level/events/__init__.py +++ b/kdb-bot/src/modules/level/events/__init__.py @@ -11,11 +11,11 @@ Discord bot for the Keksdose discord Server """ -__title__ = 'modules.base.events' +__title__ = 'modules.level.events' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/level/service/__init__.py b/kdb-bot/src/modules/level/service/__init__.py index 425ab6c1..4ed3b99f 100644 --- a/kdb-bot/src/modules/level/service/__init__.py +++ b/kdb-bot/src/modules/level/service/__init__.py @@ -1 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'modules.level.service' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.3.dev25' + +from collections import namedtuple + + # imports + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/moderator/__init__.py b/kdb-bot/src/modules/moderator/__init__.py index 2582b00b..ab63ed9f 100644 --- a/kdb-bot/src/modules/moderator/__init__.py +++ b/kdb-bot/src/modules/moderator/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.moderator' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/moderator/command/__init__.py b/kdb-bot/src/modules/moderator/command/__init__.py index bdd9ddbd..68d0c50b 100644 --- a/kdb-bot/src/modules/moderator/command/__init__.py +++ b/kdb-bot/src/modules/moderator/command/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.moderator.command' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/permission/__init__.py b/kdb-bot/src/modules/permission/__init__.py index 151c7175..22153513 100644 --- a/kdb-bot/src/modules/permission/__init__.py +++ b/kdb-bot/src/modules/permission/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.permission' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/permission/abc/__init__.py b/kdb-bot/src/modules/permission/abc/__init__.py index 460f1ab5..294d277c 100644 --- a/kdb-bot/src/modules/permission/abc/__init__.py +++ b/kdb-bot/src/modules/permission/abc/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.permission.abc' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/permission/configuration/__init__.py b/kdb-bot/src/modules/permission/configuration/__init__.py index 062b17ee..2a385a0d 100644 --- a/kdb-bot/src/modules/permission/configuration/__init__.py +++ b/kdb-bot/src/modules/permission/configuration/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.permission.configuration' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/permission/events/__init__.py b/kdb-bot/src/modules/permission/events/__init__.py index 32488e5a..d51f9b12 100644 --- a/kdb-bot/src/modules/permission/events/__init__.py +++ b/kdb-bot/src/modules/permission/events/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.permission.events' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports: VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/permission/service/__init__.py b/kdb-bot/src/modules/permission/service/__init__.py index ddc1eb80..930246c1 100644 --- a/kdb-bot/src/modules/permission/service/__init__.py +++ b/kdb-bot/src/modules/permission/service/__init__.py @@ -15,7 +15,7 @@ __title__ = 'modules.permission.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev70' +__version__ = '0.3.dev25' from collections import namedtuple @@ -23,4 +23,4 @@ from collections import namedtuple # imports VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev70') +version_info = VersionInfo(major='0', minor='3', micro='dev25') From 3d27295b14324933d1a6341bccbce0a0a6b98261 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 22:43:42 +0100 Subject: [PATCH 119/275] Added translation to level service #25 --- kdb-bot/src/bot/translation/de.json | 3 +++ kdb-bot/src/modules/level/service/level_service.py | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 206df3ea..447b232e 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -149,6 +149,9 @@ "boot_log": { "login_message": "Ich bin on the line :D\nDer Scheiß hat {} Sekunden gedauert" }, + "level": { + "new_level_message": "<@{}> ist nun Level {}" + }, "database": {}, "permission": { } diff --git a/kdb-bot/src/modules/level/service/level_service.py b/kdb-bot/src/modules/level/service/level_service.py index 6385b9ae..bf53b11c 100644 --- a/kdb-bot/src/modules/level/service/level_service.py +++ b/kdb-bot/src/modules/level/service/level_service.py @@ -6,6 +6,7 @@ from cpl_core.database.context import DatabaseContextABC from cpl_core.logging import LoggerABC from cpl_discord.container import Guild, Role, Member from cpl_discord.service import DiscordBotServiceABC +from cpl_translation import TranslatePipe from bot_core.service.message_service import MessageService from bot_data.model.level import Level @@ -27,7 +28,8 @@ class LevelService: users: UserRepositoryService, servers: ServerRepositoryService, bot: DiscordBotServiceABC, - message_service: MessageService + message_service: MessageService, + t: TranslatePipe ): self._config = config self._logger = logger @@ -37,6 +39,7 @@ class LevelService: self._servers = servers self._bot = bot self._message_service = message_service + self._t = t def get_level(self, user: User) -> Level: levels = self._levels.get_levels_by_server_id(user.server.server_id).order_by(lambda l: l.min_xp) @@ -73,7 +76,7 @@ class LevelService: level_settings: LevelServerSettings = self._config.get_configuration(f'LevelServerSettings_{guild.id}') await self._message_service.send_channel_message( self._bot.get_channel(level_settings.changed_level_notification_channel), - f'<@{member.id}> ist nun Level {level.name}', + self._t.transform('modules.level.new_level_message'.format(member.id, level.name)), is_persistent=True ) From 63000636a81b5c4bc2abe451f911dac337259733 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 6 Nov 2022 22:46:15 +0100 Subject: [PATCH 120/275] Updated version of level project #25 --- kdb-bot/src/modules/level/level.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/modules/level/level.json b/kdb-bot/src/modules/level/level.json index 1d8d7425..7ceaa225 100644 --- a/kdb-bot/src/modules/level/level.json +++ b/kdb-bot/src/modules/level/level.json @@ -2,7 +2,7 @@ "ProjectSettings": { "Name": "level", "Version": { - "Major": "0", + "Major": "1", "Minor": "0", "Micro": "0" }, From 7ed30c40be6d8ae79dae7609848ba9598c7fa5d3 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Mon, 7 Nov 2022 19:04:41 +0100 Subject: [PATCH 121/275] Added list command #29 --- kdb-bot/src/bot/translation/de.json | 30 ++++--- kdb-bot/src/modules/level/command/__init__.py | 26 ++++++ .../src/modules/level/command/level_group.py | 89 +++++++++++++++++++ kdb-bot/src/modules/level/level_module.py | 5 ++ 4 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 kdb-bot/src/modules/level/command/__init__.py create mode 100644 kdb-bot/src/modules/level/command/level_group.py diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 447b232e..18549e58 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -150,7 +150,17 @@ "login_message": "Ich bin on the line :D\nDer Scheiß hat {} Sekunden gedauert" }, "level": { - "new_level_message": "<@{}> ist nun Level {}" + "new_level_message": "<@{}> ist nun Level {}", + "error": { + "nothing_found": "Keine auto-role Einträge gefunden." + }, + "list": { + "title": "Level:", + "description": "Konfigurierte Level:", + "name": "Name", + "min_xp": "Mindest XP", + "permission_int": "Berechtigungen" + } }, "database": {}, "permission": { @@ -166,15 +176,15 @@ "message": "Dies ist eine Test-Mail vom Krümmelmonster Web Interface\nGesendet von {}-{}" } }, - "auth": { - "confirmation": { - "subject": "E-Mail für {} {} bestätigen", - "message": "Öffne den Link um die E-Mail zu bestätigen:\n{}auth/register/{}" - }, - "forgot_password": { - "subject": "Passwort für {} {} zurücksetzen", - "message": "Öffne den Link um das Passwort zu ändern:\n{}auth/forgot-password/{}" - } + "auth": { + "confirmation": { + "subject": "E-Mail für {} {} bestätigen", + "message": "Öffne den Link um die E-Mail zu bestätigen:\n{}auth/register/{}" + }, + "forgot_password": { + "subject": "Passwort für {} {} zurücksetzen", + "message": "Öffne den Link um das Passwort zu ändern:\n{}auth/forgot-password/{}" } + } } } \ No newline at end of file diff --git a/kdb-bot/src/modules/level/command/__init__.py b/kdb-bot/src/modules/level/command/__init__.py new file mode 100644 index 00000000..c95179bf --- /dev/null +++ b/kdb-bot/src/modules/level/command/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'modules.level.command' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.3.dev29' + +from collections import namedtuple + + +# imports + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='dev29') diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py new file mode 100644 index 00000000..8de28aa4 --- /dev/null +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -0,0 +1,89 @@ +import discord +from cpl_core.database.context import DatabaseContextABC +from cpl_discord.command import DiscordCommandABC +from cpl_discord.service import DiscordBotServiceABC +from cpl_translation import TranslatePipe +from discord.ext import commands +from discord.ext.commands import Context + +from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC +from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.logging.command_logger import CommandLogger +from bot_data.abc.level_repository_abc import LevelRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from modules.permission.abc.permission_service_abc import PermissionServiceABC + + +class LevelGroup(DiscordCommandABC): + + def __init__( + self, + logger: CommandLogger, + message_service: MessageServiceABC, + bot: DiscordBotServiceABC, + client_utils: ClientUtilsServiceABC, + permission_service: PermissionServiceABC, + translate: TranslatePipe, + db_context: DatabaseContextABC, + levels: LevelRepositoryABC, + servers: ServerRepositoryABC, + ): + DiscordCommandABC.__init__(self) + + self._logger = logger + self._message_service = message_service + self._bot = bot + self._client_utils = client_utils + self._permissions = permission_service + self._t = translate + self._db_context = db_context + self._levels = levels + self._servers = servers + + self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') + + @commands.hybrid_group() + @commands.guild_only() + async def level(self, ctx: Context): + pass + + @level.command(alias='levels') + @commands.guild_only() + async def list(self, ctx: Context, wait: int = None): + self._logger.debug(__name__, f'Received command level list {ctx}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_moderator(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command level list') + return + + if ctx.guild is None: + return + + server = self._servers.get_server_by_discord_id(ctx.guild.id) + levels = self._levels.get_levels_by_server_id(server.server_id) + if levels.count() < 1: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.error.nothing_found')) + self._logger.trace(__name__, f'Finished command level list') + return + + level_name = '' + xp = '' + permissions = '' + for level in levels: + level_name += f'\n{level.name}' + xp += f'\n{level.min_xp}' + permissions += f'\n{level.permissions}' + + embed = discord.Embed( + title=self._t.transform('modules.level.list.title'), + description=self._t.transform('modules.level.list.description'), + color=int('ef9d0d', 16) + ) + embed.add_field(name=self._t.transform('modules.level.list.name'), value=level_name, inline=True) + embed.add_field(name=self._t.transform('modules.level.list.min_xp'), value=xp, inline=True) + embed.add_field(name=self._t.transform('modules.level.list.permission_int'), value=permissions, inline=True) + await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait) + self._logger.trace(__name__, f'Finished command level list') diff --git a/kdb-bot/src/modules/level/level_module.py b/kdb-bot/src/modules/level/level_module.py index 1943e73c..aeb57409 100644 --- a/kdb-bot/src/modules/level/level_module.py +++ b/kdb-bot/src/modules/level/level_module.py @@ -8,6 +8,7 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from modules.level.command.level_group import LevelGroup from modules.level.events.level_on_message_event import LevelOnMessageEvent from modules.level.events.level_on_voice_state_update_event import LevelOnVoiceStateUpdateEvent from modules.level.level_seeder import LevelSeeder @@ -29,5 +30,9 @@ class LevelModule(ModuleABC): services.add_transient(LevelSeeder) services.add_transient(LevelService) + # commands + self._dc.add_command(LevelGroup) + + # events self._dc.add_event(DiscordEventTypesEnum.on_message.value, LevelOnMessageEvent) self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, LevelOnVoiceStateUpdateEvent) From b2397fcc2ff815042ec0480e3d42b2a7b97f0033 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Mon, 7 Nov 2022 20:35:12 +0100 Subject: [PATCH 122/275] Added create command #90 --- kdb-bot/src/bot/translation/de.json | 35 ++++++- .../src/modules/level/command/level_group.py | 97 +++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 18549e58..994ec206 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -45,6 +45,31 @@ "no_entry_point_error": "Fehler: Kein Eintrittspunkt!", "extension_failed": "Fehler: Erweiterung ist fehlgeschlagen!", "bot_not_ready_yet": "Ey Alter! Gedulde dich doch mal! ..." + }, + "colors": { + "blue": "Blau", + "dark_blue": "Dunkelblau", + "dark_gold": "Dunkelgold", + "dark_gray": "Dunkelgrau", + "dark_green": "Dunkelgrün", + "dark_grey": "Dunkelgrau", + "dark_magenta": "Dunkelmagenta", + "dark_orange": "Dunkelorange", + "dark_purple": "Dunkelviolett", + "dark_red": "Dunkelrot", + "dark_teal": "Dunkelzinnoberrot", + "default": "Standard", + "gold": "Gold", + "green": "Grün", + "greyple": "Graugrün", + "light_gray": "Hellgrau", + "light_grey": "Hellgrau", + "magenta": "Magenta", + "orange": "Orange", + "purple": "Violett", + "red": "Rot", + "teal": "Aquamarin", + "yellow": "Gelb" } }, "modules": { @@ -152,7 +177,9 @@ "level": { "new_level_message": "<@{}> ist nun Level {}", "error": { - "nothing_found": "Keine auto-role Einträge gefunden." + "nothing_found": "Keine Level Einträge gefunden.", + "level_with_name_already_exists": "Ein Level mit dem Namen {} existiert bereits!", + "level_with_xp_already_exists": "Das Level {} hat bereits die Mindest XP {}!" }, "list": { "title": "Level:", @@ -160,6 +187,12 @@ "name": "Name", "min_xp": "Mindest XP", "permission_int": "Berechtigungen" + }, + "create": { + "created": "Level {} mit Berechtigungen {} wurde erstellt.", + "seeding_started": "Levelsystem wird neu geladen.", + "seeding_failed": "Levelsystem konnte nicht neu geladen werden.", + "seeding_finished": "Levelsystem wurde Erfolgreich neu geladen." } }, "database": {}, diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 8de28aa4..5dd951c0 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -1,8 +1,11 @@ +from typing import List as TList + import discord from cpl_core.database.context import DatabaseContextABC from cpl_discord.command import DiscordCommandABC from cpl_discord.service import DiscordBotServiceABC from cpl_translation import TranslatePipe +from discord import app_commands from discord.ext import commands from discord.ext.commands import Context @@ -11,6 +14,9 @@ from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.logging.command_logger import CommandLogger from bot_data.abc.level_repository_abc import LevelRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.model.level import Level +from modules.level.level_seeder import LevelSeeder +from modules.level.service.level_service import LevelService from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -27,6 +33,8 @@ class LevelGroup(DiscordCommandABC): db_context: DatabaseContextABC, levels: LevelRepositoryABC, servers: ServerRepositoryABC, + level_service: LevelService, + level_seeder: LevelSeeder, ): DiscordCommandABC.__init__(self) @@ -40,6 +48,9 @@ class LevelGroup(DiscordCommandABC): self._levels = levels self._servers = servers + self._level_service = level_service + self._level_seeder = level_seeder + self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') @commands.hybrid_group() @@ -87,3 +98,89 @@ class LevelGroup(DiscordCommandABC): embed.add_field(name=self._t.transform('modules.level.list.permission_int'), value=permissions, inline=True) await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait) self._logger.trace(__name__, f'Finished command level list') + + @level.command() + @commands.guild_only() + async def create(self, ctx: Context, name: str, color: str, min_xp: int, permissions: int): + self._logger.debug(__name__, f'Received command level create {ctx}') + + try: + color = hex(discord.Colour.from_str(color).value) + except Exception as e: + self._logger.error(__name__, f'Error parsing color {color}', e) + return + + try: + permissions = discord.Permissions(permissions).value + except Exception as e: + self._logger.error(__name__, f'Error parsing permissions {permissions}', e) + return + + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if ctx.guild is None: + return + + server = self._servers.get_server_by_discord_id(ctx.guild.id) + level = Level(name, color, min_xp, permissions, server) + levels = self._levels.get_levels_by_server_id(server.server_id) + + if levels.where(lambda l: l.name == level.name).first_or_default() is not None: + self._logger.debug(__name__, f'Level with name {level.name} already exists') + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.error.level_with_name_already_exists').format(level.name)) + elif levels.where(lambda l: l.min_xp == level.min_xp).first_or_default() is not None: + self._logger.debug(__name__, f'Level with min_xp {level.min_xp} already exists {level.name}') + found_level = levels.where(lambda l: l.min_xp == level.min_xp).first_or_default() + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.error.level_with_xp_already_exists').format(found_level.name, found_level.min_xp)) + else: + try: + self._levels.add_level(level) + self._db_context.save_changes() + self._logger.info(__name__, f'Saved level {name} with color {color}, min_xp {min_xp} and permissions {permissions}') + except Exception as e: + self._logger.error(__name__, f'Could not save level {name} with color {color}, min_xp {min_xp} and permissions {permissions}', e) + else: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.create.created').format(name, permissions)) + + # send message to ctx.channel because send_ctx_msg resolves ctx + try: + await self._message_service.send_channel_message(ctx.channel, self._t.transform('modules.level.create.seeding_started')) + await self._level_seeder.seed() + await self._message_service.send_channel_message(ctx.channel, self._t.transform('modules.level.create.seeding_finished')) + except Exception as e: + self._logger.error(__name__, f'Level seeding failed', e) + await self._message_service.send_channel_message(ctx.channel, self._t.transform('modules.level.create.seeding_failed')) + + self._logger.trace(__name__, f'Finished command level create') + + @create.autocomplete('color') + async def create_color_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + colors = [ + ('blue', discord.Colour.blue().to_rgb()), + ('dark_blue', discord.Colour.dark_blue().to_rgb()), + ('dark_gold', discord.Colour.dark_gold().to_rgb()), + ('dark_gray', discord.Colour.dark_gray().to_rgb()), + ('dark_green', discord.Colour.dark_green().to_rgb()), + ('dark_grey', discord.Colour.dark_grey().to_rgb()), + ('dark_magenta', discord.Colour.dark_magenta().to_rgb()), + ('dark_orange', discord.Colour.dark_orange().to_rgb()), + ('dark_purple', discord.Colour.dark_purple().to_rgb()), + ('dark_red', discord.Colour.dark_red().to_rgb()), + ('dark_teal', discord.Colour.dark_teal().to_rgb()), + ('default', discord.Colour.default().to_rgb()), + ('gold', discord.Colour.gold().to_rgb()), + ('green', discord.Colour.green().to_rgb()), + ('greyple', discord.Colour.greyple().to_rgb()), + ('light_gray', discord.Colour.light_gray().to_rgb()), + ('light_grey', discord.Colour.light_grey().to_rgb()), + ('magenta', discord.Colour.magenta().to_rgb()), + ('orange', discord.Colour.orange().to_rgb()), + ('purple', discord.Colour.purple().to_rgb()), + ('red', discord.Colour.red().to_rgb()), + ('teal', discord.Colour.teal().to_rgb()), + ('yellow', discord.Colour.yellow().to_rgb()) + ] + # value in rg format see: + # https://discordpy.readthedocs.io/en/latest/api.html#discord.Colour.to_rgb + return [app_commands.Choice(name=self._t.transform(f'common.colors.{color}'), value=f'rgb({code[0]}, {code[1]}, {code[2]})') for color, code in colors] From 6a18b15447682122351db4974e72e70689c42871 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Mon, 7 Nov 2022 20:35:24 +0100 Subject: [PATCH 123/275] Fixed level stuff #90 --- kdb-bot/src/modules/level/level_seeder.py | 12 ++++++++++-- kdb-bot/src/modules/level/service/level_service.py | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py index 9a1148bb..870a7b30 100644 --- a/kdb-bot/src/modules/level/level_seeder.py +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -1,3 +1,4 @@ +import discord from cpl_discord.container import Guild, Role from cpl_discord.service import DiscordBotServiceABC from cpl_query.extension import List @@ -26,14 +27,20 @@ class LevelSeeder(DataSeederABC): self._default_levels = levels.levels.order_by_descending(lambda l: l.min_xp) async def _create_level(self, level: Level, guild: Guild, server: Server): + level.server = server try: if guild.roles.where(lambda r: r.name == level.name).first_or_default() is not None: return await guild.create_role(name=level.name, colour=Colour(int(level.color, 16)), hoist=False, mentionable=True, permissions=Permissions(level.permissions)) self._logger.info(__name__, f'Created level {level.name}') - level.server = server - self._levels.add_level(level) + + if self._levels.find_levels_by_server_id(server.server_id).where(lambda l: l == level).first_or_default() is not None: + self._levels.add_level(level) + except discord.errors.Forbidden as e: + self._logger.error(__name__, f'Creating level failed', e) + level.permissions = 0 + self._levels.update_level(level) except Exception as e: self._logger.error(__name__, f'Creating level failed', e) @@ -66,6 +73,7 @@ class LevelSeeder(DataSeederABC): if created_default: continue + levels = levels.order_by_descending(lambda l: l.min_xp) position_above_levels = guild.roles.where(lambda r: r.name == self._level_header).single().position for role in guild.roles.order_by_descending(lambda r: r.position): if levels.where(lambda l: l.name == role.name).count() == 0: diff --git a/kdb-bot/src/modules/level/service/level_service.py b/kdb-bot/src/modules/level/service/level_service.py index bf53b11c..f47102b9 100644 --- a/kdb-bot/src/modules/level/service/level_service.py +++ b/kdb-bot/src/modules/level/service/level_service.py @@ -76,7 +76,7 @@ class LevelService: level_settings: LevelServerSettings = self._config.get_configuration(f'LevelServerSettings_{guild.id}') await self._message_service.send_channel_message( self._bot.get_channel(level_settings.changed_level_notification_channel), - self._t.transform('modules.level.new_level_message'.format(member.id, level.name)), + self._t.transform('modules.level.new_level_message').format(member.id, level.name), is_persistent=True ) From b8fcaa48a52835e8767cff753ba9ca842cfb9479 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Mon, 7 Nov 2022 20:54:27 +0100 Subject: [PATCH 124/275] Added remove command #91 --- kdb-bot/src/bot/translation/de.json | 14 +++- .../src/modules/level/command/level_group.py | 79 ++++++++++++++++--- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 994ec206..cabd346b 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -176,6 +176,9 @@ }, "level": { "new_level_message": "<@{}> ist nun Level {}", + "seeding_started": "Levelsystem wird neu geladen.", + "seeding_failed": "Levelsystem konnte nicht neu geladen werden.", + "seeding_finished": "Levelsystem wurde Erfolgreich neu geladen.", "error": { "nothing_found": "Keine Level Einträge gefunden.", "level_with_name_already_exists": "Ein Level mit dem Namen {} existiert bereits!", @@ -189,10 +192,13 @@ "permission_int": "Berechtigungen" }, "create": { - "created": "Level {} mit Berechtigungen {} wurde erstellt.", - "seeding_started": "Levelsystem wird neu geladen.", - "seeding_failed": "Levelsystem konnte nicht neu geladen werden.", - "seeding_finished": "Levelsystem wurde Erfolgreich neu geladen." + "created": "Level {} mit Berechtigungen {} wurde erstellt." + }, + "remove": { + "success": "Level {} wurde entfernt :D", + "error": { + "not_found": "Level {} nicht gefunden!" + } } }, "database": {}, diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 5dd951c0..05cdcad7 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -3,6 +3,7 @@ from typing import List as TList import discord from cpl_core.database.context import DatabaseContextABC from cpl_discord.command import DiscordCommandABC +from cpl_discord.container import Guild, Role from cpl_discord.service import DiscordBotServiceABC from cpl_translation import TranslatePipe from discord import app_commands @@ -53,6 +54,16 @@ class LevelGroup(DiscordCommandABC): self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') + async def _seed_levels(self, channel: discord.TextChannel): + # send message to ctx.channel because send_ctx_msg resolves ctx + try: + await self._message_service.send_channel_message(channel, self._t.transform('modules.level.seeding_started')) + await self._level_seeder.seed() + await self._message_service.send_channel_message(channel, self._t.transform('modules.level.seeding_finished')) + except Exception as e: + self._logger.error(__name__, f'Level seeding failed', e) + await self._message_service.send_channel_message(channel, self._t.transform('modules.level.seeding_failed')) + @commands.hybrid_group() @commands.guild_only() async def level(self, ctx: Context): @@ -104,6 +115,14 @@ class LevelGroup(DiscordCommandABC): async def create(self, ctx: Context, name: str, color: str, min_xp: int, permissions: int): self._logger.debug(__name__, f'Received command level create {ctx}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_admin(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command level remove') + return + try: color = hex(discord.Colour.from_str(color).value) except Exception as e: @@ -116,9 +135,6 @@ class LevelGroup(DiscordCommandABC): self._logger.error(__name__, f'Error parsing permissions {permissions}', e) return - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - if ctx.guild is None: return @@ -142,15 +158,7 @@ class LevelGroup(DiscordCommandABC): self._logger.error(__name__, f'Could not save level {name} with color {color}, min_xp {min_xp} and permissions {permissions}', e) else: await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.create.created').format(name, permissions)) - - # send message to ctx.channel because send_ctx_msg resolves ctx - try: - await self._message_service.send_channel_message(ctx.channel, self._t.transform('modules.level.create.seeding_started')) - await self._level_seeder.seed() - await self._message_service.send_channel_message(ctx.channel, self._t.transform('modules.level.create.seeding_finished')) - except Exception as e: - self._logger.error(__name__, f'Level seeding failed', e) - await self._message_service.send_channel_message(ctx.channel, self._t.transform('modules.level.create.seeding_failed')) + await self._seed_levels(ctx.channel) self._logger.trace(__name__, f'Finished command level create') @@ -184,3 +192,50 @@ class LevelGroup(DiscordCommandABC): # value in rg format see: # https://discordpy.readthedocs.io/en/latest/api.html#discord.Colour.to_rgb return [app_commands.Choice(name=self._t.transform(f'common.colors.{color}'), value=f'rgb({code[0]}, {code[1]}, {code[2]})') for color, code in colors] + + @level.command() + @commands.guild_only() + async def remove(self, ctx: Context, level: str): + self._logger.debug(__name__, f'Received command level remove {ctx}') + + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_admin(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command level remove') + return + + if ctx.guild is None: + self._logger.trace(__name__, f'Finished command level remove') + return + + server = self._servers.get_server_by_discord_id(ctx.guild.id) + level_from_db = self._levels.get_levels_by_server_id(server.server_id).where(lambda l: l.name == level).first_or_default() + if level_from_db is None: + self._logger.debug(__name__, f'level {level} not found') + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.remove.error.not_found').format(level)) + self._logger.trace(__name__, f'Finished command level remove') + return + + try: + self._levels.delete_level(level_from_db) + self._db_context.save_changes() + guild: Guild = self._bot.guilds.where(lambda g: g == ctx.guild).single() + role: Role = guild.roles.where(lambda r: r.name == level).single_or_default() + if role is not None: + await role.delete() + self._logger.info(__name__, f'Removed level {level}') + except Exception as e: + self._logger.error(__name__, f'Could not remove level {level}', e) + else: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.remove.success').format(level)) + await self._seed_levels(ctx.channel) + + self._logger.trace(__name__, f'Finished command level remove') + + @remove.autocomplete('level') + async def remove_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + server = self._servers.get_server_by_discord_id(interaction.guild.id) + levels = self._levels.get_levels_by_server_id(server.server_id).select(lambda l: l.name) + return [app_commands.Choice(name=level, value=level) for level in levels] From 898b24c8ee802526014c578cdb9bb24acc132246 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 08:01:57 +0100 Subject: [PATCH 125/275] Changed color names #90 --- kdb-bot/src/bot/translation/de.json | 5 ++--- kdb-bot/src/modules/level/command/level_group.py | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 994ec206..92874768 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -57,18 +57,17 @@ "dark_orange": "Dunkelorange", "dark_purple": "Dunkelviolett", "dark_red": "Dunkelrot", - "dark_teal": "Dunkelzinnoberrot", + "dark_teal": "Dunkelblaugrün", "default": "Standard", "gold": "Gold", "green": "Grün", "greyple": "Graugrün", "light_gray": "Hellgrau", - "light_grey": "Hellgrau", "magenta": "Magenta", "orange": "Orange", "purple": "Violett", "red": "Rot", - "teal": "Aquamarin", + "teal": "Blaugrün", "yellow": "Gelb" } }, diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 5dd951c0..0eaf7d35 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -172,7 +172,6 @@ class LevelGroup(DiscordCommandABC): ('gold', discord.Colour.gold().to_rgb()), ('green', discord.Colour.green().to_rgb()), ('greyple', discord.Colour.greyple().to_rgb()), - ('light_gray', discord.Colour.light_gray().to_rgb()), ('light_grey', discord.Colour.light_grey().to_rgb()), ('magenta', discord.Colour.magenta().to_rgb()), ('orange', discord.Colour.orange().to_rgb()), From 7f2cdc5b1c3d75a43a1b5a08cc182124e9200caf Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Mon, 7 Nov 2022 20:54:27 +0100 Subject: [PATCH 126/275] Added remove command #91 --- kdb-bot/src/bot/translation/de.json | 14 +++- .../src/modules/level/command/level_group.py | 79 ++++++++++++++++--- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 92874768..ad81d335 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -175,6 +175,9 @@ }, "level": { "new_level_message": "<@{}> ist nun Level {}", + "seeding_started": "Levelsystem wird neu geladen.", + "seeding_failed": "Levelsystem konnte nicht neu geladen werden.", + "seeding_finished": "Levelsystem wurde Erfolgreich neu geladen.", "error": { "nothing_found": "Keine Level Einträge gefunden.", "level_with_name_already_exists": "Ein Level mit dem Namen {} existiert bereits!", @@ -188,10 +191,13 @@ "permission_int": "Berechtigungen" }, "create": { - "created": "Level {} mit Berechtigungen {} wurde erstellt.", - "seeding_started": "Levelsystem wird neu geladen.", - "seeding_failed": "Levelsystem konnte nicht neu geladen werden.", - "seeding_finished": "Levelsystem wurde Erfolgreich neu geladen." + "created": "Level {} mit Berechtigungen {} wurde erstellt." + }, + "remove": { + "success": "Level {} wurde entfernt :D", + "error": { + "not_found": "Level {} nicht gefunden!" + } } }, "database": {}, diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 0eaf7d35..7658da5a 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -3,6 +3,7 @@ from typing import List as TList import discord from cpl_core.database.context import DatabaseContextABC from cpl_discord.command import DiscordCommandABC +from cpl_discord.container import Guild, Role from cpl_discord.service import DiscordBotServiceABC from cpl_translation import TranslatePipe from discord import app_commands @@ -53,6 +54,16 @@ class LevelGroup(DiscordCommandABC): self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') + async def _seed_levels(self, channel: discord.TextChannel): + # send message to ctx.channel because send_ctx_msg resolves ctx + try: + await self._message_service.send_channel_message(channel, self._t.transform('modules.level.seeding_started')) + await self._level_seeder.seed() + await self._message_service.send_channel_message(channel, self._t.transform('modules.level.seeding_finished')) + except Exception as e: + self._logger.error(__name__, f'Level seeding failed', e) + await self._message_service.send_channel_message(channel, self._t.transform('modules.level.seeding_failed')) + @commands.hybrid_group() @commands.guild_only() async def level(self, ctx: Context): @@ -104,6 +115,14 @@ class LevelGroup(DiscordCommandABC): async def create(self, ctx: Context, name: str, color: str, min_xp: int, permissions: int): self._logger.debug(__name__, f'Received command level create {ctx}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_admin(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command level remove') + return + try: color = hex(discord.Colour.from_str(color).value) except Exception as e: @@ -116,9 +135,6 @@ class LevelGroup(DiscordCommandABC): self._logger.error(__name__, f'Error parsing permissions {permissions}', e) return - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - if ctx.guild is None: return @@ -142,15 +158,7 @@ class LevelGroup(DiscordCommandABC): self._logger.error(__name__, f'Could not save level {name} with color {color}, min_xp {min_xp} and permissions {permissions}', e) else: await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.create.created').format(name, permissions)) - - # send message to ctx.channel because send_ctx_msg resolves ctx - try: - await self._message_service.send_channel_message(ctx.channel, self._t.transform('modules.level.create.seeding_started')) - await self._level_seeder.seed() - await self._message_service.send_channel_message(ctx.channel, self._t.transform('modules.level.create.seeding_finished')) - except Exception as e: - self._logger.error(__name__, f'Level seeding failed', e) - await self._message_service.send_channel_message(ctx.channel, self._t.transform('modules.level.create.seeding_failed')) + await self._seed_levels(ctx.channel) self._logger.trace(__name__, f'Finished command level create') @@ -183,3 +191,50 @@ class LevelGroup(DiscordCommandABC): # value in rg format see: # https://discordpy.readthedocs.io/en/latest/api.html#discord.Colour.to_rgb return [app_commands.Choice(name=self._t.transform(f'common.colors.{color}'), value=f'rgb({code[0]}, {code[1]}, {code[2]})') for color, code in colors] + + @level.command() + @commands.guild_only() + async def remove(self, ctx: Context, level: str): + self._logger.debug(__name__, f'Received command level remove {ctx}') + + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_admin(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command level remove') + return + + if ctx.guild is None: + self._logger.trace(__name__, f'Finished command level remove') + return + + server = self._servers.get_server_by_discord_id(ctx.guild.id) + level_from_db = self._levels.get_levels_by_server_id(server.server_id).where(lambda l: l.name == level).first_or_default() + if level_from_db is None: + self._logger.debug(__name__, f'level {level} not found') + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.remove.error.not_found').format(level)) + self._logger.trace(__name__, f'Finished command level remove') + return + + try: + self._levels.delete_level(level_from_db) + self._db_context.save_changes() + guild: Guild = self._bot.guilds.where(lambda g: g == ctx.guild).single() + role: Role = guild.roles.where(lambda r: r.name == level).single_or_default() + if role is not None: + await role.delete() + self._logger.info(__name__, f'Removed level {level}') + except Exception as e: + self._logger.error(__name__, f'Could not remove level {level}', e) + else: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.remove.success').format(level)) + await self._seed_levels(ctx.channel) + + self._logger.trace(__name__, f'Finished command level remove') + + @remove.autocomplete('level') + async def remove_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + server = self._servers.get_server_by_discord_id(interaction.guild.id) + levels = self._levels.get_levels_by_server_id(server.server_id).select(lambda l: l.name) + return [app_commands.Choice(name=level, value=level) for level in levels] From fe32604a146ec71fe1ccb720db6e1b27d62dedf5 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 17:49:32 +0100 Subject: [PATCH 127/275] Added level down command #28 --- .../bot/config/appsettings.edrafts-lapi.json | 9 +++- kdb-bot/src/bot/translation/de.json | 5 ++ .../src/modules/level/command/level_group.py | 47 +++++++++++++++++-- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/kdb-bot/src/bot/config/appsettings.edrafts-lapi.json b/kdb-bot/src/bot/config/appsettings.edrafts-lapi.json index 2e9e52a3..e912b148 100644 --- a/kdb-bot/src/bot/config/appsettings.edrafts-lapi.json +++ b/kdb-bot/src/bot/config/appsettings.edrafts-lapi.json @@ -8,7 +8,7 @@ "LoggingSettings": { "Path": "logs/", "Filename": "bot.log", - "ConsoleLogLevel": "TRACE", + "ConsoleLogLevel": "DEBUG", "FileLogLevel": "TRACE" }, "BotLoggingSettings": { @@ -76,7 +76,12 @@ }, "BootLog": { "910199451145076828": { - "LoginMessageChannelId": "910199452915093588" + "LoginMessageChannelId": 910199452915093588 + } + }, + "Level": { + "910199451145076828": { + "ChangedLevelNotificationChannelId": 910199452667637892 } }, "Permission": { diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 92874768..e66eeb02 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -192,6 +192,11 @@ "seeding_started": "Levelsystem wird neu geladen.", "seeding_failed": "Levelsystem konnte nicht neu geladen werden.", "seeding_finished": "Levelsystem wurde Erfolgreich neu geladen." + }, + "down": { + "already_first": "{} hat bereits das erste Level.", + "success": "{} wurde auf Level {} runtergesetzt :)", + "failed": "{} konnte nicht runtergesetzt werden :(" } }, "database": {}, diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 0eaf7d35..26d03045 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -14,6 +14,7 @@ from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.logging.command_logger import CommandLogger from bot_data.abc.level_repository_abc import LevelRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.model.level import Level from modules.level.level_seeder import LevelSeeder from modules.level.service.level_service import LevelService @@ -30,9 +31,10 @@ class LevelGroup(DiscordCommandABC): client_utils: ClientUtilsServiceABC, permission_service: PermissionServiceABC, translate: TranslatePipe, - db_context: DatabaseContextABC, + db: DatabaseContextABC, levels: LevelRepositoryABC, servers: ServerRepositoryABC, + users: UserRepositoryABC, level_service: LevelService, level_seeder: LevelSeeder, ): @@ -44,9 +46,10 @@ class LevelGroup(DiscordCommandABC): self._client_utils = client_utils self._permissions = permission_service self._t = translate - self._db_context = db_context + self._db = db self._levels = levels self._servers = servers + self._users = users self._level_service = level_service self._level_seeder = level_seeder @@ -136,7 +139,7 @@ class LevelGroup(DiscordCommandABC): else: try: self._levels.add_level(level) - self._db_context.save_changes() + self._db.save_changes() self._logger.info(__name__, f'Saved level {name} with color {color}, min_xp {min_xp} and permissions {permissions}') except Exception as e: self._logger.error(__name__, f'Could not save level {name} with color {color}, min_xp {min_xp} and permissions {permissions}', e) @@ -183,3 +186,41 @@ class LevelGroup(DiscordCommandABC): # value in rg format see: # https://discordpy.readthedocs.io/en/latest/api.html#discord.Colour.to_rgb return [app_commands.Choice(name=self._t.transform(f'common.colors.{color}'), value=f'rgb({code[0]}, {code[1]}, {code[2]})') for color, code in colors] + + @level.command() + @commands.guild_only() + async def down(self, ctx: Context, member: discord.Member): + self._logger.debug(__name__, f'Received command level down {ctx} {member}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_moderator(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command level list') + return + + if ctx.guild is None: + return + + server = self._servers.get_server_by_discord_id(ctx.guild.id) + user = self._users.get_user_by_discord_id_and_server_id(member.id, server.server_id) + level = self._level_service.get_level(user) + levels = self._levels.get_levels_by_server_id(server.server_id).order_by(lambda l: l.min_xp) + + if level == levels.first(): + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.down.already_first').format(member.name)) + self._logger.trace(__name__, f'Finished command level list') + return + + try: + new_level = levels.where(lambda l: l.min_xp < level.min_xp).last() + user.xp = new_level.min_xp + self._users.update_user(user) + self._db.save_changes() + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.down.success').format(member.name, new_level.name)) + await self._level_service.set_level(user) + except Exception as e: + self._logger.error(__name__, f'Cannot level down {member.name} with level {level.name}', e) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.down.failed').format(member.name)) + + self._logger.trace(__name__, f'Finished command level down') From faaadb000914575544ec75c8395e1241015fcf34 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 17:49:42 +0100 Subject: [PATCH 128/275] Fixed level seeder #28 --- kdb-bot/src/modules/level/level_seeder.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py index 870a7b30..2b4fc643 100644 --- a/kdb-bot/src/modules/level/level_seeder.py +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -29,14 +29,14 @@ class LevelSeeder(DataSeederABC): async def _create_level(self, level: Level, guild: Guild, server: Server): level.server = server try: - if guild.roles.where(lambda r: r.name == level.name).first_or_default() is not None: - return + if guild.roles.where(lambda r: r.name == level.name).first_or_default() is None: + await guild.create_role(name=level.name, colour=Colour(int(level.color, 16)), hoist=False, mentionable=True, permissions=Permissions(level.permissions)) + self._logger.debug(__name__, f'Created role {level.name}') - await guild.create_role(name=level.name, colour=Colour(int(level.color, 16)), hoist=False, mentionable=True, permissions=Permissions(level.permissions)) - self._logger.info(__name__, f'Created level {level.name}') - - if self._levels.find_levels_by_server_id(server.server_id).where(lambda l: l == level).first_or_default() is not None: + levels = self._levels.find_levels_by_server_id(server.server_id) + if levels is None or levels.where(lambda l: l == level).first_or_default() is None: self._levels.add_level(level) + self._logger.debug(__name__, f'Saved level {level.name}') except discord.errors.Forbidden as e: self._logger.error(__name__, f'Creating level failed', e) level.permissions = 0 From f0f23163e45129070e47d059812ef3b3928f336b Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 18:01:04 +0100 Subject: [PATCH 129/275] Added level down command #27 --- kdb-bot/src/bot/translation/de.json | 5 ++ .../src/modules/level/command/level_group.py | 48 ++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index e66eeb02..146ea6c7 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -197,6 +197,11 @@ "already_first": "{} hat bereits das erste Level.", "success": "{} wurde auf Level {} runtergesetzt :)", "failed": "{} konnte nicht runtergesetzt werden :(" + }, + "up": { + "already_last": "{} hat bereits das höchste Level.", + "success": "{} wurde auf Level {} hochgesetzt :)", + "failed": "{} konnte nicht hochgesetzt werden :(" } }, "database": {}, diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 26d03045..8175c976 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -196,12 +196,15 @@ class LevelGroup(DiscordCommandABC): if not self._permissions.is_member_moderator(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command level list') + self._logger.trace(__name__, f'Finished command level down') return if ctx.guild is None: return + if member.bot: + return + server = self._servers.get_server_by_discord_id(ctx.guild.id) user = self._users.get_user_by_discord_id_and_server_id(member.id, server.server_id) level = self._level_service.get_level(user) @@ -209,7 +212,7 @@ class LevelGroup(DiscordCommandABC): if level == levels.first(): await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.down.already_first').format(member.name)) - self._logger.trace(__name__, f'Finished command level list') + self._logger.trace(__name__, f'Finished command level down') return try: @@ -224,3 +227,44 @@ class LevelGroup(DiscordCommandABC): await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.down.failed').format(member.name)) self._logger.trace(__name__, f'Finished command level down') + + @level.command() + @commands.guild_only() + async def up(self, ctx: Context, member: discord.Member): + self._logger.debug(__name__, f'Received command level up {ctx} {member}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_moderator(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command level up') + return + + if ctx.guild is None: + return + + if member.bot: + return + + server = self._servers.get_server_by_discord_id(ctx.guild.id) + user = self._users.get_user_by_discord_id_and_server_id(member.id, server.server_id) + level = self._level_service.get_level(user) + levels = self._levels.get_levels_by_server_id(server.server_id).order_by(lambda l: l.min_xp) + + if level.name == levels.last().name: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.up.already_last').format(member.name)) + self._logger.trace(__name__, f'Finished command level up') + return + + try: + new_level = levels.where(lambda l: l.min_xp > level.min_xp).first() + user.xp = new_level.min_xp + self._users.update_user(user) + self._db.save_changes() + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.up.success').format(member.name, new_level.name)) + await self._level_service.set_level(user) + except Exception as e: + self._logger.error(__name__, f'Cannot level up {member.name} with level {level.name}', e) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.up.failed').format(member.name)) + + self._logger.trace(__name__, f'Finished command level up') From e5eb50a3cbff82f8bfda6c94c96ce09cefdac663 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 18:49:10 +0100 Subject: [PATCH 130/275] Fixed level seeder #26 --- kdb-bot/src/modules/level/level_seeder.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py index 2b4fc643..8e6eb9e7 100644 --- a/kdb-bot/src/modules/level/level_seeder.py +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -1,7 +1,6 @@ import discord -from cpl_discord.container import Guild, Role +from cpl_discord.container import Guild from cpl_discord.service import DiscordBotServiceABC -from cpl_query.extension import List from discord import Permissions, Colour from bot_core.logging.database_logger import DatabaseLogger @@ -34,7 +33,7 @@ class LevelSeeder(DataSeederABC): self._logger.debug(__name__, f'Created role {level.name}') levels = self._levels.find_levels_by_server_id(server.server_id) - if levels is None or levels.where(lambda l: l == level).first_or_default() is None: + if levels is None or levels.where(lambda l: l.name == level.name).first_or_default() is None: self._levels.add_level(level) self._logger.debug(__name__, f'Saved level {level.name}') except discord.errors.Forbidden as e: From c666ceb51ed0b1964b969d39836384cc8bde76db Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 18:49:18 +0100 Subject: [PATCH 131/275] Added level set command #26 --- kdb-bot/src/bot/translation/de.json | 5 ++ .../src/modules/level/command/level_group.py | 51 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 146ea6c7..ea8eb111 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -202,6 +202,11 @@ "already_last": "{} hat bereits das höchste Level.", "success": "{} wurde auf Level {} hochgesetzt :)", "failed": "{} konnte nicht hochgesetzt werden :(" + }, + "set": { + "already_level": "{} hat bereits das Level {} :/", + "success": "{} ist nun Level {} :)", + "failed": "Das Level von {} konnte nicht auf {} gesetzt werden :(" } }, "database": {}, diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 8175c976..241971b9 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -268,3 +268,54 @@ class LevelGroup(DiscordCommandABC): await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.up.failed').format(member.name)) self._logger.trace(__name__, f'Finished command level up') + + @level.command() + @commands.guild_only() + async def set(self, ctx: Context, member: discord.Member, level: str): + self._logger.debug(__name__, f'Received command level up {ctx} {member}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_moderator(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command level up') + return + + if ctx.guild is None: + return + + if member.bot: + return + + server = self._servers.get_server_by_discord_id(ctx.guild.id) + user = self._users.get_user_by_discord_id_and_server_id(member.id, server.server_id) + current_level = self._level_service.get_level(user) + new_level = self._levels.get_levels_by_server_id(server.server_id).where(lambda l: l.name == level).single_or_default() + + if new_level is None: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.set.failed').format(member.name, level)) + self._logger.trace(__name__, f'Finished command level set') + return + + if current_level.name == level: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.set.already_level').format(member.name, level)) + self._logger.trace(__name__, f'Finished command level set') + return + + try: + user.xp = new_level.min_xp + self._users.update_user(user) + self._db.save_changes() + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.set.success').format(member.name, new_level.name)) + await self._level_service.set_level(user) + except Exception as e: + self._logger.error(__name__, f'Cannot set level {level} for {member.name}', e) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.set.failed').format(member.name)) + + self._logger.trace(__name__, f'Finished command level set') + + @set.autocomplete('level') + async def set_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + server = self._servers.get_server_by_discord_id(interaction.guild.id) + levels = self._levels.get_levels_by_server_id(server.server_id).select(lambda l: l.name) + return [app_commands.Choice(name=level, value=level) for level in levels] From 9099ebe4f30a0f90b79c4e450cd3e9c5c9a5477b Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 18:59:24 +0100 Subject: [PATCH 132/275] Moved admin & moderator commands to base module --- kdb-bot/cpl-workspace.json | 2 - kdb-bot/src/bot/module_list.py | 4 -- kdb-bot/src/modules/admin/__init__.py | 26 ----------- kdb-bot/src/modules/admin/admin.json | 44 ------------------- kdb-bot/src/modules/admin/admin_module.py | 24 ---------- kdb-bot/src/modules/admin/command/__init__.py | 26 ----------- kdb-bot/src/modules/base/base_module.py | 9 ++++ .../command/purge_command.py | 0 .../command/restart_command.py | 0 .../command/shutdown_command.py | 0 .../{moderator => base}/command/user_group.py | 0 kdb-bot/src/modules/moderator/__init__.py | 26 ----------- .../src/modules/moderator/command/__init__.py | 26 ----------- kdb-bot/src/modules/moderator/moderator.json | 44 ------------------- .../src/modules/moderator/moderator_module.py | 24 ---------- 15 files changed, 9 insertions(+), 246 deletions(-) delete mode 100644 kdb-bot/src/modules/admin/__init__.py delete mode 100644 kdb-bot/src/modules/admin/admin.json delete mode 100644 kdb-bot/src/modules/admin/admin_module.py delete mode 100644 kdb-bot/src/modules/admin/command/__init__.py rename kdb-bot/src/modules/{moderator => base}/command/purge_command.py (100%) rename kdb-bot/src/modules/{admin => base}/command/restart_command.py (100%) rename kdb-bot/src/modules/{admin => base}/command/shutdown_command.py (100%) rename kdb-bot/src/modules/{moderator => base}/command/user_group.py (100%) delete mode 100644 kdb-bot/src/modules/moderator/__init__.py delete mode 100644 kdb-bot/src/modules/moderator/command/__init__.py delete mode 100644 kdb-bot/src/modules/moderator/moderator.json delete mode 100644 kdb-bot/src/modules/moderator/moderator_module.py diff --git a/kdb-bot/cpl-workspace.json b/kdb-bot/cpl-workspace.json index 7af45bfe..fa9a4332 100644 --- a/kdb-bot/cpl-workspace.json +++ b/kdb-bot/cpl-workspace.json @@ -5,13 +5,11 @@ "bot": "src/bot/bot.json", "bot-core": "src/bot_core/bot-core.json", "bot-data": "src/bot_data/bot-data.json", - "admin": "src/modules/admin/admin.json", "auto-role": "src/modules/auto_role/auto-role.json", "base": "src/modules/base/base.json", "boot-log": "src/modules/boot_log/boot-log.json", "database": "src/modules/database/database.json", "level": "src/modules/level/level.json", - "moderator": "src/modules/moderator/moderator.json", "permission": "src/modules/permission/permission.json", "bot-api": "src/bot_api/bot-api.json", "get-version": "tools/get_version/get-version.json", diff --git a/kdb-bot/src/bot/module_list.py b/kdb-bot/src/bot/module_list.py index 03b8307c..575824f3 100644 --- a/kdb-bot/src/bot/module_list.py +++ b/kdb-bot/src/bot/module_list.py @@ -4,13 +4,11 @@ from bot_api.api_module import ApiModule from bot_core.core_extension.core_extension_module import CoreExtensionModule from bot_core.core_module import CoreModule from bot_data.data_module import DataModule -from modules.admin.admin_module import AdminModule from modules.auto_role.auto_role_module import AutoRoleModule from modules.base.base_module import BaseModule from modules.boot_log.boot_log_module import BootLogModule from modules.database.database_module import DatabaseModule from modules.level.level_module import LevelModule -from modules.moderator.moderator_module import ModeratorModule from modules.permission.permission_module import PermissionModule @@ -22,12 +20,10 @@ class ModuleList: return List(type, [ CoreModule, # has to be first! DataModule, - AdminModule, AutoRoleModule, BaseModule, DatabaseModule, LevelModule, - ModeratorModule, PermissionModule, ApiModule, # has to be last! diff --git a/kdb-bot/src/modules/admin/__init__.py b/kdb-bot/src/modules/admin/__init__.py deleted file mode 100644 index afcd237e..00000000 --- a/kdb-bot/src/modules/admin/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -bot Keksdose bot -~~~~~~~~~~~~~~~~~~~ - -Discord bot for the Keksdose discord Server - -:copyright: (c) 2022 sh-edraft.de -:license: MIT, see LICENSE for more details. - -""" - -__title__ = 'modules.admin' -__author__ = 'Sven Heidemann' -__license__ = 'MIT' -__copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev25' - -from collections import namedtuple - - -# imports: - -VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/admin/admin.json b/kdb-bot/src/modules/admin/admin.json deleted file mode 100644 index 38e06428..00000000 --- a/kdb-bot/src/modules/admin/admin.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "ProjectSettings": { - "Name": "admin", - "Version": { - "Major": "1", - "Minor": "0", - "Micro": "0" - }, - "Author": "", - "AuthorEmail": "", - "Description": "", - "LongDescription": "", - "URL": "", - "CopyrightDate": "", - "CopyrightName": "", - "LicenseName": "", - "LicenseDescription": "", - "Dependencies": [ - "cpl-core>=2022.10.0.post5" - ], - "DevDependencies": [ - "cpl-cli==2022.10.0" - ], - "PythonVersion": ">=3.10.4", - "PythonPath": {}, - "Classifiers": [] - }, - "BuildSettings": { - "ProjectType": "library", - "SourcePath": "", - "OutputPath": "../../dist", - "Main": "admin.main", - "EntryPoint": "admin", - "IncludePackageData": false, - "Included": [], - "Excluded": [ - "*/__pycache__", - "*/logs", - "*/tests" - ], - "PackageData": {}, - "ProjectReferences": [] - } -} \ No newline at end of file diff --git a/kdb-bot/src/modules/admin/admin_module.py b/kdb-bot/src/modules/admin/admin_module.py deleted file mode 100644 index 48f76afa..00000000 --- a/kdb-bot/src/modules/admin/admin_module.py +++ /dev/null @@ -1,24 +0,0 @@ -from cpl_core.configuration import ConfigurationABC -from cpl_core.dependency_injection import ServiceCollectionABC -from cpl_core.environment import ApplicationEnvironmentABC -from cpl_discord.service.discord_collection_abc import DiscordCollectionABC - -from bot_core.abc.module_abc import ModuleABC -from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum -from modules.admin.command.restart_command import RestartCommand -from modules.admin.command.shutdown_command import ShutdownCommand - - -class AdminModule(ModuleABC): - - def __init__(self, dc: DiscordCollectionABC): - ModuleABC.__init__(self, dc, FeatureFlagsEnum.admin_module) - - def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): - pass - - def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): - # commands - self._dc.add_command(RestartCommand) - self._dc.add_command(ShutdownCommand) - # events diff --git a/kdb-bot/src/modules/admin/command/__init__.py b/kdb-bot/src/modules/admin/command/__init__.py deleted file mode 100644 index ed0cff3b..00000000 --- a/kdb-bot/src/modules/admin/command/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -bot Keksdose bot -~~~~~~~~~~~~~~~~~~~ - -Discord bot for the Keksdose discord Server - -:copyright: (c) 2022 sh-edraft.de -:license: MIT, see LICENSE for more details. - -""" - -__title__ = 'modules.admin.command' -__author__ = 'Sven Heidemann' -__license__ = 'MIT' -__copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev25' - -from collections import namedtuple - - -# imports: - -VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/base/base_module.py b/kdb-bot/src/modules/base/base_module.py index 4ea1ed85..4383e58a 100644 --- a/kdb-bot/src/modules/base/base_module.py +++ b/kdb-bot/src/modules/base/base_module.py @@ -11,6 +11,10 @@ from modules.base.command.afk_command import AFKCommand from modules.base.command.help_command import HelpCommand from modules.base.command.info_command import InfoCommand from modules.base.command.ping_command import PingCommand +from modules.base.command.purge_command import PurgeCommand +from modules.base.command.restart_command import RestartCommand +from modules.base.command.shutdown_command import ShutdownCommand +from modules.base.command.user_group import UserGroup from modules.base.events.base_on_command_error_event import BaseOnCommandErrorEvent from modules.base.events.base_on_command_event import BaseOnCommandEvent from modules.base.events.base_on_member_join_event import BaseOnMemberJoinEvent @@ -35,6 +39,11 @@ class BaseModule(ModuleABC): self._dc.add_command(HelpCommand) self._dc.add_command(InfoCommand) self._dc.add_command(PingCommand) + + self._dc.add_command(RestartCommand) + self._dc.add_command(ShutdownCommand) + self._dc.add_command(PurgeCommand) + self._dc.add_command(UserGroup) # events self._dc.add_event(DiscordEventTypesEnum.on_command.value, BaseOnCommandEvent) self._dc.add_event(DiscordEventTypesEnum.on_command_error.value, BaseOnCommandErrorEvent) diff --git a/kdb-bot/src/modules/moderator/command/purge_command.py b/kdb-bot/src/modules/base/command/purge_command.py similarity index 100% rename from kdb-bot/src/modules/moderator/command/purge_command.py rename to kdb-bot/src/modules/base/command/purge_command.py diff --git a/kdb-bot/src/modules/admin/command/restart_command.py b/kdb-bot/src/modules/base/command/restart_command.py similarity index 100% rename from kdb-bot/src/modules/admin/command/restart_command.py rename to kdb-bot/src/modules/base/command/restart_command.py diff --git a/kdb-bot/src/modules/admin/command/shutdown_command.py b/kdb-bot/src/modules/base/command/shutdown_command.py similarity index 100% rename from kdb-bot/src/modules/admin/command/shutdown_command.py rename to kdb-bot/src/modules/base/command/shutdown_command.py diff --git a/kdb-bot/src/modules/moderator/command/user_group.py b/kdb-bot/src/modules/base/command/user_group.py similarity index 100% rename from kdb-bot/src/modules/moderator/command/user_group.py rename to kdb-bot/src/modules/base/command/user_group.py diff --git a/kdb-bot/src/modules/moderator/__init__.py b/kdb-bot/src/modules/moderator/__init__.py deleted file mode 100644 index ab63ed9f..00000000 --- a/kdb-bot/src/modules/moderator/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -bot Keksdose bot -~~~~~~~~~~~~~~~~~~~ - -Discord bot for the Keksdose discord Server - -:copyright: (c) 2022 sh-edraft.de -:license: MIT, see LICENSE for more details. - -""" - -__title__ = 'modules.moderator' -__author__ = 'Sven Heidemann' -__license__ = 'MIT' -__copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev25' - -from collections import namedtuple - - -# imports: - -VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/moderator/command/__init__.py b/kdb-bot/src/modules/moderator/command/__init__.py deleted file mode 100644 index 68d0c50b..00000000 --- a/kdb-bot/src/modules/moderator/command/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -bot Keksdose bot -~~~~~~~~~~~~~~~~~~~ - -Discord bot for the Keksdose discord Server - -:copyright: (c) 2022 sh-edraft.de -:license: MIT, see LICENSE for more details. - -""" - -__title__ = 'modules.moderator.command' -__author__ = 'Sven Heidemann' -__license__ = 'MIT' -__copyright__ = 'Copyright (c) 2022 sh-edraft.de' -__version__ = '0.3.dev25' - -from collections import namedtuple - - -# imports: - -VersionInfo = namedtuple('VersionInfo', 'major minor micro') -version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/moderator/moderator.json b/kdb-bot/src/modules/moderator/moderator.json deleted file mode 100644 index b92b06bc..00000000 --- a/kdb-bot/src/modules/moderator/moderator.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "ProjectSettings": { - "Name": "moderator", - "Version": { - "Major": "1", - "Minor": "0", - "Micro": "0" - }, - "Author": "", - "AuthorEmail": "", - "Description": "", - "LongDescription": "", - "URL": "", - "CopyrightDate": "", - "CopyrightName": "", - "LicenseName": "", - "LicenseDescription": "", - "Dependencies": [ - "cpl-core>=2022.10.0.post5" - ], - "DevDependencies": [ - "cpl-cli==2022.10.0" - ], - "PythonVersion": ">=3.10.4", - "PythonPath": {}, - "Classifiers": [] - }, - "BuildSettings": { - "ProjectType": "library", - "SourcePath": "", - "OutputPath": "../../dist", - "Main": "moderator.main", - "EntryPoint": "moderator", - "IncludePackageData": false, - "Included": [], - "Excluded": [ - "*/__pycache__", - "*/logs", - "*/tests" - ], - "PackageData": {}, - "ProjectReferences": [] - } -} \ No newline at end of file diff --git a/kdb-bot/src/modules/moderator/moderator_module.py b/kdb-bot/src/modules/moderator/moderator_module.py deleted file mode 100644 index 7df1865f..00000000 --- a/kdb-bot/src/modules/moderator/moderator_module.py +++ /dev/null @@ -1,24 +0,0 @@ -from cpl_core.configuration import ConfigurationABC -from cpl_core.dependency_injection import ServiceCollectionABC -from cpl_core.environment import ApplicationEnvironmentABC -from cpl_discord.service.discord_collection_abc import DiscordCollectionABC - -from bot_core.abc.module_abc import ModuleABC -from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum -from modules.moderator.command.purge_command import PurgeCommand -from modules.moderator.command.user_group import UserGroup - - -class ModeratorModule(ModuleABC): - - def __init__(self, dc: DiscordCollectionABC): - ModuleABC.__init__(self, dc, FeatureFlagsEnum.moderator_module) - - def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): - pass - - def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): - # commands - self._dc.add_command(PurgeCommand) - self._dc.add_command(UserGroup) - # events From f24fd5e88001c91bd52d4844e9a1c527cc6cd7ce Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 20:28:38 +0100 Subject: [PATCH 133/275] Added logic to call statistics #46 --- kdb-bot/src/bot/translation/de.json | 12 +- kdb-bot/src/modules/stats/__init__.py | 1 + kdb-bot/src/modules/stats/command/__init__.py | 1 + .../src/modules/stats/command/stats_group.py | 133 ++++++++++++++++++ kdb-bot/src/modules/stats/model/__init__.py | 1 + kdb-bot/src/modules/stats/model/statistic.py | 21 +++ .../modules/stats/model/statistic_result.py | 24 ++++ kdb-bot/src/modules/stats/service/__init__.py | 1 + .../stats/service/statistic_service.py | 69 +++++++++ kdb-bot/src/modules/stats/stats.json | 46 ++++++ kdb-bot/src/modules/stats/test/user_xp_asc.py | 37 +++++ .../src/modules/stats/test/user_xp_desc.py | 37 +++++ 12 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 kdb-bot/src/modules/stats/__init__.py create mode 100644 kdb-bot/src/modules/stats/command/__init__.py create mode 100644 kdb-bot/src/modules/stats/command/stats_group.py create mode 100644 kdb-bot/src/modules/stats/model/__init__.py create mode 100644 kdb-bot/src/modules/stats/model/statistic.py create mode 100644 kdb-bot/src/modules/stats/model/statistic_result.py create mode 100644 kdb-bot/src/modules/stats/service/__init__.py create mode 100644 kdb-bot/src/modules/stats/service/statistic_service.py create mode 100644 kdb-bot/src/modules/stats/stats.json create mode 100644 kdb-bot/src/modules/stats/test/user_xp_asc.py create mode 100644 kdb-bot/src/modules/stats/test/user_xp_desc.py diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 92874768..8ed8bd84 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -195,7 +195,17 @@ } }, "database": {}, - "permission": { + "permission": {}, + "stats": { + "list": { + "statistic": "Statistik", + "description": "Beschreibung" + }, + "view": { + "statistic": "Statistik", + "description": "Beschreibung", + "failed": "Statistik kann nicht gezeigt werden :(" + } } }, "api": { diff --git a/kdb-bot/src/modules/stats/__init__.py b/kdb-bot/src/modules/stats/__init__.py new file mode 100644 index 00000000..ad5eca30 --- /dev/null +++ b/kdb-bot/src/modules/stats/__init__.py @@ -0,0 +1 @@ +# imports: diff --git a/kdb-bot/src/modules/stats/command/__init__.py b/kdb-bot/src/modules/stats/command/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/kdb-bot/src/modules/stats/command/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py new file mode 100644 index 00000000..32b32f61 --- /dev/null +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -0,0 +1,133 @@ +from typing import List as TList + +import discord +from cpl_discord.command import DiscordCommandABC +from cpl_query.extension import List +from cpl_translation import TranslatePipe +from discord import app_commands +from discord.ext import commands +from discord.ext.commands import Context + +from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC +from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.logging.command_logger import CommandLogger +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from modules.permission.abc.permission_service_abc import PermissionServiceABC +from modules.stats.model.statistic import Statistic +from modules.stats.service.statistic_service import StatisticService +from modules.stats.test.user_xp_asc import user_xp_asc +from modules.stats.test.user_xp_desc import user_xp_desc + + +class StatsGroup(DiscordCommandABC): + + def __init__( + self, + logger: CommandLogger, + message_service: MessageServiceABC, + client_utils: ClientUtilsServiceABC, + translate: TranslatePipe, + permission_service: PermissionServiceABC, + statistic: StatisticService, + servers: ServerRepositoryABC, + ): + DiscordCommandABC.__init__(self) + + self._logger = logger + self._client_utils = client_utils + self._message_service = message_service + self._t = translate + self._permissions = permission_service + self._statistic = statistic + self._servers = servers + + self._stats = List(Statistic, [ + Statistic('Benutzer XP Aufsteigend', 'Zeigt XP von jedem Benutzer, aufsteigend nach XP', user_xp_asc), + Statistic('Benutzer XP Absteigend', 'Zeigt XP von jedem Benutzer, absteigend nach XP', user_xp_desc) + ]) + + @commands.hybrid_group() + @commands.guild_only() + async def stats(self, ctx: Context): + pass + + @stats.command(alias='rules') + @commands.guild_only() + async def list(self, ctx: Context, wait: int = None): + self._logger.debug(__name__, f'Received command stats list {ctx}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_moderator(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command stats list') + return + + embed = discord.Embed( + title=self._t.transform('modules.auto_role.list.title'), + description=self._t.transform('modules.auto_role.list.description'), + color=int('ef9d0d', 16) + ) + + statistics = '' + descriptions = '' + for statistic in self._stats: + statistics += f'\n{statistic["Name"]}' + descriptions += f'\n{statistic["Description"]}' + + embed.add_field(name=self._t.transform('modules.stats.list.statistic'), value=statistics, inline=True) + embed.add_field(name=self._t.transform('modules.stats.list.description'), value=statistics, inline=True) + await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait) + self._logger.trace(__name__, f'Finished command stats list') + + @stats.command() + @commands.guild_only() + async def view(self, ctx: Context, name: str, wait: int = None): + self._logger.debug(__name__, f'Received command stats {ctx}:{name}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if ctx.guild is None: + return + + try: + server = self._servers.get_server_by_discord_id(ctx.guild.id) + statistic = self._stats.where(lambda s: s.name == name).single() + result = await self._statistic.execute(statistic.func, server) + + # headers = '' + # rows = '' + # for header in result.header: + # headers += f'\n{header}' + # + # for row in result.values: + # row_str = '' + # for column in row: + # row_str += f'\n{column}' + # rows += f'\n{row_str}' + + embed = discord.Embed( + title=statistic.name, + description=statistic.description, + color=int('ef9d0d', 16) + ) + + for i in range(result.header.count()): + header = result.header[i] + value = '' + for row in result.values: + value += f'\n{row[i]}' + embed.add_field(name=header, value=value, inline=True) + + # embed.add_field(name=self._t.transform('modules.auto_role.list.message_id'), value=rows, inline=True) + await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait) + # await self._message_service.send_ctx_msg(ctx, name, wait_before_delete=wait) + except Exception as e: + self._logger.error(__name__, f'Cannot view statistic {name}', e) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.view.failed')) + + self._logger.trace(__name__, f'Finished stats command') + + @view.autocomplete('name') + async def view_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in self._stats] diff --git a/kdb-bot/src/modules/stats/model/__init__.py b/kdb-bot/src/modules/stats/model/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/kdb-bot/src/modules/stats/model/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/stats/model/statistic.py b/kdb-bot/src/modules/stats/model/statistic.py new file mode 100644 index 00000000..2db1b75d --- /dev/null +++ b/kdb-bot/src/modules/stats/model/statistic.py @@ -0,0 +1,21 @@ +from typing import Callable + + +class Statistic: + + def __init__(self, name: str, description: str, func: Callable): + self._name = name + self._description = description + self._func = func + + @property + def name(self) -> str: + return self._name + + @property + def description(self) -> str: + return self._description + + @property + def func(self) -> Callable: + return self._func diff --git a/kdb-bot/src/modules/stats/model/statistic_result.py b/kdb-bot/src/modules/stats/model/statistic_result.py new file mode 100644 index 00000000..c2c53742 --- /dev/null +++ b/kdb-bot/src/modules/stats/model/statistic_result.py @@ -0,0 +1,24 @@ +from cpl_query.extension import List + + +class StatisticResult: + + def __init__(self): + self._header = List(str) + self._values = List(List) + + @property + def header(self) -> List[str]: + return self._header + + @header.setter + def header(self, value: List[str]): + self._header = value + + @property + def values(self) -> List[List]: + return self._values + + @values.setter + def values(self, value: List[List]): + self._values = value diff --git a/kdb-bot/src/modules/stats/service/__init__.py b/kdb-bot/src/modules/stats/service/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/kdb-bot/src/modules/stats/service/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/stats/service/statistic_service.py b/kdb-bot/src/modules/stats/service/statistic_service.py new file mode 100644 index 00000000..b0f59d62 --- /dev/null +++ b/kdb-bot/src/modules/stats/service/statistic_service.py @@ -0,0 +1,69 @@ +from abc import abstractmethod +from typing import Callable + +from cpl_discord.service import DiscordBotServiceABC + +from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC +from bot_data.abc.client_repository_abc import ClientRepositoryABC +from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC +from bot_data.abc.level_repository_abc import LevelRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC +from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server import Server +from modules.stats.model.statistic_result import StatisticResult + + +class StatisticService: + + def __init__( + self, + auto_roles: AutoRoleRepositoryABC, + clients: ClientRepositoryABC, + known_users: KnownUserRepositoryABC, + levels: LevelRepositoryABC, + servers: ServerRepositoryABC, + user_joined_servers: UserJoinedServerRepositoryABC, + user_joined_voice_channel: UserJoinedVoiceChannelRepositoryABC, + users: UserRepositoryABC, + bot: DiscordBotServiceABC, + ): + self._auto_roles = auto_roles + self._clients = clients + self._known_users = known_users + self._levels = levels + self._servers = servers + self._user_joined_servers = user_joined_servers + self._user_joined_voice_channel = user_joined_voice_channel + self._users = users + self._bot = bot + + async def execute(self, _f: Callable, server: Server) -> StatisticResult: + guild = self._bot.guilds.where(lambda g: g.id == server.discord_server_id).single() + + return await _f( + self._auto_roles + .get_auto_roles() + .where(lambda x: x.server.server_id == server.server_id), + self._clients + .get_clients() + .where(lambda x: x.server.server_id == server.server_id), + self._known_users.get_users(), + self._levels + .get_levels() + .where(lambda x: x.server.server_id == server.server_id), + self._servers + .get_servers() + .where(lambda x: x.server_id == server.server_id), + self._user_joined_servers + .get_user_joined_servers() + .where(lambda x: x.user.server.server_id == server.server_id), + self._user_joined_voice_channel + .get_user_joined_voice_channels() + .where(lambda x: x.user.server.server_id == server.server_id), + self._users + .get_users() + .where(lambda x: x.server.server_id == server.server_id), + guild + ) diff --git a/kdb-bot/src/modules/stats/stats.json b/kdb-bot/src/modules/stats/stats.json new file mode 100644 index 00000000..c4fec974 --- /dev/null +++ b/kdb-bot/src/modules/stats/stats.json @@ -0,0 +1,46 @@ +{ + "ProjectSettings": { + "Name": "stats", + "Version": { + "Major": "0", + "Minor": "0", + "Micro": "0" + }, + "Author": "", + "AuthorEmail": "", + "Description": "", + "LongDescription": "", + "URL": "", + "CopyrightDate": "", + "CopyrightName": "", + "LicenseName": "", + "LicenseDescription": "", + "Dependencies": [ + "cpl-core>=2022.10.0.post7" + ], + "DevDependencies": [ + "cpl-cli>=2022.10.1" + ], + "PythonVersion": ">=3.10.4", + "PythonPath": { + "linux": "" + }, + "Classifiers": [] + }, + "BuildSettings": { + "ProjectType": "library", + "SourcePath": "", + "OutputPath": "../../dist", + "Main": "stats.main", + "EntryPoint": "stats", + "IncludePackageData": false, + "Included": [], + "Excluded": [ + "*/__pycache__", + "*/logs", + "*/tests" + ], + "PackageData": {}, + "ProjectReferences": [] + } +} \ No newline at end of file diff --git a/kdb-bot/src/modules/stats/test/user_xp_asc.py b/kdb-bot/src/modules/stats/test/user_xp_asc.py new file mode 100644 index 00000000..9ee1219e --- /dev/null +++ b/kdb-bot/src/modules/stats/test/user_xp_asc.py @@ -0,0 +1,37 @@ +from cpl_discord.container import Guild +from cpl_query.extension import List + +from bot_data.model.auto_role import AutoRole +from bot_data.model.client import Client +from bot_data.model.known_user import KnownUser +from bot_data.model.level import Level +from bot_data.model.server import Server +from bot_data.model.user import User +from bot_data.model.user_joined_server import UserJoinedServer +from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel +from modules.stats.model.statistic_result import StatisticResult + + +async def user_xp_asc( + auto_roles: List[AutoRole], + clients: List[Client], + known_users: List[KnownUser], + levels: List[Level], + servers: List[Server], + user_joined_servers: List[UserJoinedServer], + user_joined_voice_channel: List[UserJoinedVoiceChannel], + users: List[User], + guild: Guild +) -> StatisticResult: + result = StatisticResult() + result.header.append('Name') + result.header.append('XP') + + for user in users.order_by(lambda u: u.xp): + row = List(str) + member = guild.get_member(user.discord_id) + row.append(member.name) + row.append(str(user.xp)) + result.values.append(row) + + return result diff --git a/kdb-bot/src/modules/stats/test/user_xp_desc.py b/kdb-bot/src/modules/stats/test/user_xp_desc.py new file mode 100644 index 00000000..32cd0f3e --- /dev/null +++ b/kdb-bot/src/modules/stats/test/user_xp_desc.py @@ -0,0 +1,37 @@ +from cpl_discord.container import Guild +from cpl_query.extension import List + +from bot_data.model.auto_role import AutoRole +from bot_data.model.client import Client +from bot_data.model.known_user import KnownUser +from bot_data.model.level import Level +from bot_data.model.server import Server +from bot_data.model.user import User +from bot_data.model.user_joined_server import UserJoinedServer +from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel +from modules.stats.model.statistic_result import StatisticResult + + +async def user_xp_desc( + auto_roles: List[AutoRole], + clients: List[Client], + known_users: List[KnownUser], + levels: List[Level], + servers: List[Server], + user_joined_servers: List[UserJoinedServer], + user_joined_voice_channel: List[UserJoinedVoiceChannel], + users: List[User], + guild: Guild +) -> StatisticResult: + result = StatisticResult() + result.header.append('Name') + result.header.append('XP') + + for user in users.order_by_descending(lambda u: u.xp): + row = List(str) + member = guild.get_member(user.discord_id) + row.append(member.name) + row.append(str(user.xp)) + result.values.append(row) + + return result From 5dca2e5fed1bf9b7b2ec57cece49906617e2e47d Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 20:28:44 +0100 Subject: [PATCH 134/275] Added logic to call statistics #46 --- kdb-bot/cpl-workspace.json | 3 ++- kdb-bot/src/bot/module_list.py | 2 ++ kdb-bot/src/bot_api/abc/__init__.py | 2 +- kdb-bot/src/bot_core/abc/__init__.py | 2 +- .../configuration/feature_flags_enum.py | 2 +- .../configuration/feature_flags_settings.py | 1 + kdb-bot/src/bot_data/abc/__init__.py | 2 +- kdb-bot/src/modules/base/abc/__init__.py | 2 +- .../src/modules/permission/abc/__init__.py | 2 +- kdb-bot/src/modules/stats/stats_module.py | 24 +++++++++++++++++++ kdb-bot/src/modules/stats/test/__init__.py | 0 11 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 kdb-bot/src/modules/stats/stats_module.py create mode 100644 kdb-bot/src/modules/stats/test/__init__.py diff --git a/kdb-bot/cpl-workspace.json b/kdb-bot/cpl-workspace.json index fa9a4332..6587cbf6 100644 --- a/kdb-bot/cpl-workspace.json +++ b/kdb-bot/cpl-workspace.json @@ -3,6 +3,7 @@ "DefaultProject": "bot", "Projects": { "bot": "src/bot/bot.json", + "bot-api": "src/bot_api/bot-api.json", "bot-core": "src/bot_core/bot-core.json", "bot-data": "src/bot_data/bot-data.json", "auto-role": "src/modules/auto_role/auto-role.json", @@ -11,7 +12,7 @@ "database": "src/modules/database/database.json", "level": "src/modules/level/level.json", "permission": "src/modules/permission/permission.json", - "bot-api": "src/bot_api/bot-api.json", + "stats": "src/modules/stats/stats.json", "get-version": "tools/get_version/get-version.json", "post-build": "tools/post_build/post-build.json", "set-version": "tools/set_version/set-version.json" diff --git a/kdb-bot/src/bot/module_list.py b/kdb-bot/src/bot/module_list.py index 575824f3..3eb60cd3 100644 --- a/kdb-bot/src/bot/module_list.py +++ b/kdb-bot/src/bot/module_list.py @@ -10,6 +10,7 @@ from modules.boot_log.boot_log_module import BootLogModule from modules.database.database_module import DatabaseModule from modules.level.level_module import LevelModule from modules.permission.permission_module import PermissionModule +from modules.stats.stats_module import StatsModule class ModuleList: @@ -26,6 +27,7 @@ class ModuleList: LevelModule, PermissionModule, ApiModule, + StatsModule, # has to be last! BootLogModule, CoreExtensionModule, diff --git a/kdb-bot/src/bot_api/abc/__init__.py b/kdb-bot/src/bot_api/abc/__init__.py index 20368eb6..ad4cf626 100644 --- a/kdb-bot/src/bot_api/abc/__init__.py +++ b/kdb-bot/src/bot_api/abc/__init__.py @@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server """ -__title__ = 'bot_api.abc' +__title__ = 'bot_api.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' diff --git a/kdb-bot/src/bot_core/abc/__init__.py b/kdb-bot/src/bot_core/abc/__init__.py index 4056210a..1266f4f1 100644 --- a/kdb-bot/src/bot_core/abc/__init__.py +++ b/kdb-bot/src/bot_core/abc/__init__.py @@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server """ -__title__ = 'bot_core.abc' +__title__ = 'bot_core.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' diff --git a/kdb-bot/src/bot_core/configuration/feature_flags_enum.py b/kdb-bot/src/bot_core/configuration/feature_flags_enum.py index 226ca1d8..f04e5ca7 100644 --- a/kdb-bot/src/bot_core/configuration/feature_flags_enum.py +++ b/kdb-bot/src/bot_core/configuration/feature_flags_enum.py @@ -2,7 +2,6 @@ from enum import Enum class FeatureFlagsEnum(Enum): - # modules api_module = 'ApiModule' admin_module = 'AdminModule' @@ -16,6 +15,7 @@ class FeatureFlagsEnum(Enum): level_module = 'LevelModule' moderator_module = 'ModeratorModule' permission_module = 'PermissionModule' + stats_module = 'StatsModule' # features api_only = 'ApiOnly' presence = 'Presence' diff --git a/kdb-bot/src/bot_core/configuration/feature_flags_settings.py b/kdb-bot/src/bot_core/configuration/feature_flags_settings.py index 1861f9a1..dff28c6e 100644 --- a/kdb-bot/src/bot_core/configuration/feature_flags_settings.py +++ b/kdb-bot/src/bot_core/configuration/feature_flags_settings.py @@ -25,6 +25,7 @@ class FeatureFlagsSettings(ConfigurationModelABC): FeatureFlagsEnum.database_module.value: True, # 02.10.2022 #48 FeatureFlagsEnum.moderator_module.value: False, # 02.10.2022 #48 FeatureFlagsEnum.permission_module.value: True, # 02.10.2022 #48 + FeatureFlagsEnum.stats_module.value: True, # 08.11.2022 #46 # features FeatureFlagsEnum.api_only.value: False, # 13.10.2022 #70 FeatureFlagsEnum.presence.value: True, # 03.10.2022 #56 diff --git a/kdb-bot/src/bot_data/abc/__init__.py b/kdb-bot/src/bot_data/abc/__init__.py index 7209be45..c2b4323a 100644 --- a/kdb-bot/src/bot_data/abc/__init__.py +++ b/kdb-bot/src/bot_data/abc/__init__.py @@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server """ -__title__ = 'bot_data.abc' +__title__ = 'bot_data.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' diff --git a/kdb-bot/src/modules/base/abc/__init__.py b/kdb-bot/src/modules/base/abc/__init__.py index ccc3e98a..41181517 100644 --- a/kdb-bot/src/modules/base/abc/__init__.py +++ b/kdb-bot/src/modules/base/abc/__init__.py @@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server """ -__title__ = 'modules.base.abc' +__title__ = 'modules.base.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' diff --git a/kdb-bot/src/modules/permission/abc/__init__.py b/kdb-bot/src/modules/permission/abc/__init__.py index 294d277c..930246c1 100644 --- a/kdb-bot/src/modules/permission/abc/__init__.py +++ b/kdb-bot/src/modules/permission/abc/__init__.py @@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server """ -__title__ = 'modules.permission.abc' +__title__ = 'modules.permission.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' diff --git a/kdb-bot/src/modules/stats/stats_module.py b/kdb-bot/src/modules/stats/stats_module.py new file mode 100644 index 00000000..4c60b02a --- /dev/null +++ b/kdb-bot/src/modules/stats/stats_module.py @@ -0,0 +1,24 @@ +from cpl_core.configuration import ConfigurationABC +from cpl_core.dependency_injection import ServiceCollectionABC +from cpl_core.environment import ApplicationEnvironmentABC +from cpl_discord.service.discord_collection_abc import DiscordCollectionABC + +from bot_core.abc.module_abc import ModuleABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from modules.stats.command.stats_group import StatsGroup +from modules.stats.service.statistic_service import StatisticService + + +class StatsModule(ModuleABC): + + def __init__(self, dc: DiscordCollectionABC): + ModuleABC.__init__(self, dc, FeatureFlagsEnum.stats_module) + + def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): + pass + + def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): + services.add_transient(StatisticService) + # commands + self._dc.add_command(StatsGroup) + # events diff --git a/kdb-bot/src/modules/stats/test/__init__.py b/kdb-bot/src/modules/stats/test/__init__.py new file mode 100644 index 00000000..e69de29b From 9e937f17b71887c522b8565cae3f81abfb81fa03 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 20:33:55 +0100 Subject: [PATCH 135/275] Fixed typo in translation... bruh #91 --- kdb-bot/src/bot/translation/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index ad81d335..4812a5c1 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -177,7 +177,7 @@ "new_level_message": "<@{}> ist nun Level {}", "seeding_started": "Levelsystem wird neu geladen.", "seeding_failed": "Levelsystem konnte nicht neu geladen werden.", - "seeding_finished": "Levelsystem wurde Erfolgreich neu geladen.", + "seeding_finished": "Levelsystem wurde erfolgreich neu geladen.", "error": { "nothing_found": "Keine Level Einträge gefunden.", "level_with_name_already_exists": "Ein Level mit dem Namen {} existiert bereits!", From 95e33109fef9375fc6c543c807eb96b2d68aa813 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 20:34:47 +0100 Subject: [PATCH 136/275] Removed old code #46 --- kdb-bot/src/modules/stats/command/stats_group.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index 32b32f61..01884c7a 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -95,17 +95,6 @@ class StatsGroup(DiscordCommandABC): statistic = self._stats.where(lambda s: s.name == name).single() result = await self._statistic.execute(statistic.func, server) - # headers = '' - # rows = '' - # for header in result.header: - # headers += f'\n{header}' - # - # for row in result.values: - # row_str = '' - # for column in row: - # row_str += f'\n{column}' - # rows += f'\n{row_str}' - embed = discord.Embed( title=statistic.name, description=statistic.description, @@ -119,9 +108,7 @@ class StatsGroup(DiscordCommandABC): value += f'\n{row[i]}' embed.add_field(name=header, value=value, inline=True) - # embed.add_field(name=self._t.transform('modules.auto_role.list.message_id'), value=rows, inline=True) await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait) - # await self._message_service.send_ctx_msg(ctx, name, wait_before_delete=wait) except Exception as e: self._logger.error(__name__, f'Cannot view statistic {name}', e) await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.view.failed')) From e3d3d200d62a3ffb25a6e5af3ccd18a8a72b6e5b Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 21:54:33 +0100 Subject: [PATCH 137/275] Added logic to handle custom statistics #46 --- kdb-bot/src/bot/translation/de.json | 4 ++ .../src/bot_core/abc/message_service_abc.py | 14 +++-- .../src/bot_core/service/message_service.py | 30 +++++++++++ .../src/modules/stats/command/stats_group.py | 52 +++++++++++++++---- kdb-bot/src/modules/stats/model/statistic.py | 8 +-- .../stats/service/statistic_service.py | 33 ++++++++++-- kdb-bot/src/modules/stats/ui/__init__.py | 1 + .../modules/stats/ui/add_statistic_form.py | 35 +++++++++++++ 8 files changed, 154 insertions(+), 23 deletions(-) create mode 100644 kdb-bot/src/modules/stats/ui/__init__.py create mode 100644 kdb-bot/src/modules/stats/ui/add_statistic_form.py diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 8ed8bd84..0d6e7858 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -205,6 +205,10 @@ "statistic": "Statistik", "description": "Beschreibung", "failed": "Statistik kann nicht gezeigt werden :(" + }, + "add": { + "failed": "Statistik kann nicht hinzugefügt werden :(", + "success": "Statistik wurde hinzugefügt :D" } } }, diff --git a/kdb-bot/src/bot_core/abc/message_service_abc.py b/kdb-bot/src/bot_core/abc/message_service_abc.py index 3e524d4d..614dd1df 100644 --- a/kdb-bot/src/bot_core/abc/message_service_abc.py +++ b/kdb-bot/src/bot_core/abc/message_service_abc.py @@ -3,6 +3,7 @@ from typing import Union import discord from cpl_query.extension import List +from discord import Interaction from discord.ext.commands import Context @@ -10,18 +11,21 @@ class MessageServiceABC(ABC): @abstractmethod def __init__(self): pass - + @abstractmethod async def delete_messages(self, messages: List[discord.Message], guild_id: int, without_tracking=False): pass - + @abstractmethod async def delete_message(self, message: discord.Message, without_tracking=False): pass - + @abstractmethod async def send_channel_message(self, channel: discord.TextChannel, message: Union[str, discord.Embed], without_tracking=True): pass - + @abstractmethod async def send_dm_message(self, message: Union[str, discord.Embed], receiver: Union[discord.User, discord.Member], without_tracking=False): pass - + @abstractmethod async def send_ctx_msg(self, ctx: Context, message: Union[str, discord.Embed], file: discord.File = None, is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass + + @abstractmethod + async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass diff --git a/kdb-bot/src/bot_core/service/message_service.py b/kdb-bot/src/bot_core/service/message_service.py index 6a43f32c..5d211c59 100644 --- a/kdb-bot/src/bot_core/service/message_service.py +++ b/kdb-bot/src/bot_core/service/message_service.py @@ -6,6 +6,7 @@ from cpl_core.configuration.configuration_abc import ConfigurationABC from cpl_core.database.context.database_context_abc import DatabaseContextABC from cpl_discord.service import DiscordBotServiceABC from cpl_query.extension import List +from discord import Interaction from discord.ext.commands import Context from bot_core.abc.message_service_abc import MessageServiceABC @@ -117,3 +118,32 @@ class MessageService(MessageServiceABC): if ctx.guild is not None: await self.delete_message(msg, without_tracking) + + async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=False): + if interaction is None: + self._logger.warn(__name__, 'Message context is empty') + self._logger.debug(__name__, f'Message: {message}') + return + + self._logger.debug(__name__, f'Try to send message\t\t{message}\n\tto: {interaction.channel}') + msg = None + try: + if isinstance(message, discord.Embed): + msg = await interaction.response.send_message(embed=message) + else: + msg = await interaction.response.send_message(message) + except Exception as e: + self._logger.error(__name__, f'Send message to channel {interaction.channel.id} failed', e) + else: + self._logger.info(__name__, f'Sent message to channel {interaction.channel.id}') + if not without_tracking and interaction.guild is not None: + self._clients.append_sent_message_count(self._bot.user.id, interaction.guild.id, 1) + self._db.save_changes() + + if wait_before_delete is not None: + await asyncio.sleep(wait_before_delete) + + if is_persistent: + return + + await self.delete_message(msg, without_tracking) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index 01884c7a..bb46b0a0 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -1,3 +1,4 @@ +import textwrap from typing import List as TList import discord @@ -15,8 +16,25 @@ from bot_data.abc.server_repository_abc import ServerRepositoryABC from modules.permission.abc.permission_service_abc import PermissionServiceABC from modules.stats.model.statistic import Statistic from modules.stats.service.statistic_service import StatisticService -from modules.stats.test.user_xp_asc import user_xp_asc -from modules.stats.test.user_xp_desc import user_xp_desc +from modules.stats.ui.add_statistic_form import AddStatisticForm + +stats = List(Statistic, [ + Statistic( + 'Benutzer XP Aufsteigend', + 'Zeigt XP von jedem Benutzer, aufsteigend nach XP', + textwrap.dedent("""\ + result.header.append('Name') + result.header.append('XP') + + for user in users.order_by(lambda u: u.xp): + row = List(str) + member = guild.get_member(user.discord_id) + row.append(member.name) + row.append(str(user.xp)) + result.values.append(row) + """)), + Statistic('Benutzer XP Absteigend', 'Zeigt XP von jedem Benutzer, absteigend nach XP', '') +]) class StatsGroup(DiscordCommandABC): @@ -41,17 +59,12 @@ class StatsGroup(DiscordCommandABC): self._statistic = statistic self._servers = servers - self._stats = List(Statistic, [ - Statistic('Benutzer XP Aufsteigend', 'Zeigt XP von jedem Benutzer, aufsteigend nach XP', user_xp_asc), - Statistic('Benutzer XP Absteigend', 'Zeigt XP von jedem Benutzer, absteigend nach XP', user_xp_desc) - ]) - @commands.hybrid_group() @commands.guild_only() async def stats(self, ctx: Context): pass - @stats.command(alias='rules') + @stats.command() @commands.guild_only() async def list(self, ctx: Context, wait: int = None): self._logger.debug(__name__, f'Received command stats list {ctx}') @@ -92,8 +105,8 @@ class StatsGroup(DiscordCommandABC): try: server = self._servers.get_server_by_discord_id(ctx.guild.id) - statistic = self._stats.where(lambda s: s.name == name).single() - result = await self._statistic.execute(statistic.func, server) + statistic = stats.where(lambda s: s.name == name).single() + result = await self._statistic.execute(statistic.code, server) embed = discord.Embed( title=statistic.name, @@ -117,4 +130,21 @@ class StatsGroup(DiscordCommandABC): @view.autocomplete('name') async def view_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: - return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in self._stats] + return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] + + @stats.command() + @commands.guild_only() + async def add(self, ctx: Context, name: str): + self._logger.debug(__name__, f'Received command stats list {ctx}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_technician(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command stats list') + return + + form = AddStatisticForm(stats, name, self._message_service, self._logger, self._t) + self._logger.trace(__name__, f'Finished stats command') + self._logger.trace(__name__, f'Started stats command form') + await ctx.interaction.response.send_modal(form) diff --git a/kdb-bot/src/modules/stats/model/statistic.py b/kdb-bot/src/modules/stats/model/statistic.py index 2db1b75d..d1b6558d 100644 --- a/kdb-bot/src/modules/stats/model/statistic.py +++ b/kdb-bot/src/modules/stats/model/statistic.py @@ -3,10 +3,10 @@ from typing import Callable class Statistic: - def __init__(self, name: str, description: str, func: Callable): + def __init__(self, name: str, description: str, code: str): self._name = name self._description = description - self._func = func + self._code = code @property def name(self) -> str: @@ -17,5 +17,5 @@ class Statistic: return self._description @property - def func(self) -> Callable: - return self._func + def code(self) -> str: + return self._code diff --git a/kdb-bot/src/modules/stats/service/statistic_service.py b/kdb-bot/src/modules/stats/service/statistic_service.py index b0f59d62..cee56335 100644 --- a/kdb-bot/src/modules/stats/service/statistic_service.py +++ b/kdb-bot/src/modules/stats/service/statistic_service.py @@ -1,7 +1,8 @@ from abc import abstractmethod -from typing import Callable from cpl_discord.service import DiscordBotServiceABC +from cpl_query.extension import List +from discord import Guild from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.client_repository_abc import ClientRepositoryABC @@ -11,7 +12,14 @@ from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.auto_role import AutoRole +from bot_data.model.client import Client +from bot_data.model.known_user import KnownUser +from bot_data.model.level import Level from bot_data.model.server import Server +from bot_data.model.user import User +from bot_data.model.user_joined_server import UserJoinedServer +from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel from modules.stats.model.statistic_result import StatisticResult @@ -39,10 +47,11 @@ class StatisticService: self._users = users self._bot = bot - async def execute(self, _f: Callable, server: Server) -> StatisticResult: + async def execute(self, code: str, server: Server) -> StatisticResult: guild = self._bot.guilds.where(lambda g: g.id == server.discord_server_id).single() - return await _f( + return await self.get_data( + code, self._auto_roles .get_auto_roles() .where(lambda x: x.server.server_id == server.server_id), @@ -67,3 +76,21 @@ class StatisticService: .where(lambda x: x.server.server_id == server.server_id), guild ) + + @staticmethod + async def get_data( + code: str, + auto_roles: List[AutoRole], + clients: List[Client], + known_users: List[KnownUser], + levels: List[Level], + servers: List[Server], + user_joined_servers: List[UserJoinedServer], + user_joined_voice_channel: List[UserJoinedVoiceChannel], + users: List[User], + guild: Guild + ) -> StatisticResult: + result = StatisticResult() + exec(code) + + return result diff --git a/kdb-bot/src/modules/stats/ui/__init__.py b/kdb-bot/src/modules/stats/ui/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/kdb-bot/src/modules/stats/ui/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/stats/ui/add_statistic_form.py b/kdb-bot/src/modules/stats/ui/add_statistic_form.py new file mode 100644 index 00000000..9591fb21 --- /dev/null +++ b/kdb-bot/src/modules/stats/ui/add_statistic_form.py @@ -0,0 +1,35 @@ +import discord +from cpl_query.extension import List +from cpl_translation import TranslatePipe +from discord import ui, TextStyle + +from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.logging.command_logger import CommandLogger +from modules.stats.model.statistic import Statistic + + +class AddStatisticForm(ui.Modal): + + description = ui.TextInput(label='Beschreibung', required=True) + code = ui.TextInput(label='Code', required=True, style=TextStyle.long) + + def __init__( + self, + stats: List[Statistic], + name: str, + message_service: MessageServiceABC, + logger: CommandLogger, + t: TranslatePipe, + ): + ui.Modal.__init__(self, title=name) + + self._stats = stats + self._name = name + self._message_service = message_service + self._logger = logger + self._t = t + + async def on_submit(self, interaction: discord.Interaction): + self._stats.append(Statistic(self._name, self.description.value, self.code.value)) + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.success')) + self._logger.trace(__name__, f'Finished stats command form') From 95a64732f31e8dd8506eb8adb36e234f59cd4f14 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 22:06:36 +0100 Subject: [PATCH 138/275] Fixed typo #46 --- kdb-bot/src/modules/stats/command/stats_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index bb46b0a0..b1051209 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -84,7 +84,7 @@ class StatsGroup(DiscordCommandABC): statistics = '' descriptions = '' - for statistic in self._stats: + for statistic in stats: statistics += f'\n{statistic["Name"]}' descriptions += f'\n{statistic["Description"]}' From eaae0587543a8869887a8ef2928357765dbf04f4 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 08:51:08 +0100 Subject: [PATCH 139/275] Fixed problems since merge #28 --- kdb-bot/src/bot/translation/de.json | 2 +- kdb-bot/src/modules/level/command/level_group.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 30d45d7b..64fb23b1 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -202,7 +202,7 @@ "down": { "already_first": "{} hat bereits das erste Level.", "success": "{} wurde auf Level {} runtergesetzt :)", - "failed": "{} konnte nicht runtergesetzt werden :(" + "failed": "{} konnte nicht runtergesetzt werden :(", "created": "Level {} mit Berechtigungen {} wurde erstellt." } }, diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 5adbec60..f8e519c2 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -3,6 +3,7 @@ from typing import List as TList import discord from cpl_core.database.context import DatabaseContextABC from cpl_discord.command import DiscordCommandABC +from cpl_discord.container import Guild, Role from cpl_discord.service import DiscordBotServiceABC from cpl_translation import TranslatePipe from discord import app_commands @@ -14,6 +15,7 @@ from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.logging.command_logger import CommandLogger from bot_data.abc.level_repository_abc import LevelRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC from bot_data.model.level import Level from modules.level.level_seeder import LevelSeeder from modules.level.service.level_service import LevelService @@ -30,9 +32,10 @@ class LevelGroup(DiscordCommandABC): client_utils: ClientUtilsServiceABC, permission_service: PermissionServiceABC, translate: TranslatePipe, - db_context: DatabaseContextABC, + db: DatabaseContextABC, levels: LevelRepositoryABC, servers: ServerRepositoryABC, + users: UserRepositoryABC, level_service: LevelService, level_seeder: LevelSeeder, ): @@ -44,9 +47,10 @@ class LevelGroup(DiscordCommandABC): self._client_utils = client_utils self._permissions = permission_service self._t = translate - self._db_context = db_context + self._db = db self._levels = levels self._servers = servers + self._users = users self._level_service = level_service self._level_seeder = level_seeder @@ -136,7 +140,7 @@ class LevelGroup(DiscordCommandABC): else: try: self._levels.add_level(level) - self._db_context.save_changes() + self._db.save_changes() self._logger.info(__name__, f'Saved level {name} with color {color}, min_xp {min_xp} and permissions {permissions}') except Exception as e: self._logger.error(__name__, f'Could not save level {name} with color {color}, min_xp {min_xp} and permissions {permissions}', e) @@ -211,7 +215,7 @@ class LevelGroup(DiscordCommandABC): try: self._levels.delete_level(level_from_db) - self._db_context.save_changes() + self._db.save_changes() guild: Guild = self._bot.guilds.where(lambda g: g == ctx.guild).single() role: Role = guild.roles.where(lambda r: r.name == level).single_or_default() if role is not None: From 7e5706137e0114b38421bb36db915d6fadefa357 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 17:18:14 +0100 Subject: [PATCH 140/275] Fixed level seeder #46 --- kdb-bot/src/modules/level/level_seeder.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py index 870a7b30..e3d9a8df 100644 --- a/kdb-bot/src/modules/level/level_seeder.py +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -1,7 +1,6 @@ import discord -from cpl_discord.container import Guild, Role +from cpl_discord.container import Guild from cpl_discord.service import DiscordBotServiceABC -from cpl_query.extension import List from discord import Permissions, Colour from bot_core.logging.database_logger import DatabaseLogger @@ -35,7 +34,7 @@ class LevelSeeder(DataSeederABC): await guild.create_role(name=level.name, colour=Colour(int(level.color, 16)), hoist=False, mentionable=True, permissions=Permissions(level.permissions)) self._logger.info(__name__, f'Created level {level.name}') - if self._levels.find_levels_by_server_id(server.server_id).where(lambda l: l == level).first_or_default() is not None: + if self._levels.find_levels_by_server_id(server.server_id).where(lambda l: l.name == level.name).first_or_default() is not None: self._levels.add_level(level) except discord.errors.Forbidden as e: self._logger.error(__name__, f'Creating level failed', e) From c407c59c1ad0b7033f498a0c54a07390d91855c9 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 17:59:55 +0100 Subject: [PATCH 141/275] Added stats edit command #46 --- kdb-bot/src/bot/translation/de.json | 4 +++ .../src/bot_core/service/message_service.py | 7 ++-- .../src/modules/stats/command/stats_group.py | 32 +++++++++++++++++-- kdb-bot/src/modules/stats/model/statistic.py | 8 +++++ .../modules/stats/ui/add_statistic_form.py | 27 ++++++++++++++-- 5 files changed, 69 insertions(+), 9 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 0d6e7858..0768197a 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -209,6 +209,10 @@ "add": { "failed": "Statistik kann nicht hinzugefügt werden :(", "success": "Statistik wurde hinzugefügt :D" + }, + "edit": { + "failed": "Statistik kann nicht bearbeitet werden :(", + "success": "Statistik wurde gespeichert :D" } } }, diff --git a/kdb-bot/src/bot_core/service/message_service.py b/kdb-bot/src/bot_core/service/message_service.py index 5d211c59..04f33bfb 100644 --- a/kdb-bot/src/bot_core/service/message_service.py +++ b/kdb-bot/src/bot_core/service/message_service.py @@ -126,12 +126,11 @@ class MessageService(MessageServiceABC): return self._logger.debug(__name__, f'Try to send message\t\t{message}\n\tto: {interaction.channel}') - msg = None try: if isinstance(message, discord.Embed): - msg = await interaction.response.send_message(embed=message) + await interaction.response.send_message(embed=message) else: - msg = await interaction.response.send_message(message) + await interaction.response.send_message(message) except Exception as e: self._logger.error(__name__, f'Send message to channel {interaction.channel.id} failed', e) else: @@ -146,4 +145,4 @@ class MessageService(MessageServiceABC): if is_persistent: return - await self.delete_message(msg, without_tracking) + await self.delete_message(await interaction.original_response(), without_tracking) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index b1051209..58105c0b 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -135,16 +135,42 @@ class StatsGroup(DiscordCommandABC): @stats.command() @commands.guild_only() async def add(self, ctx: Context, name: str): - self._logger.debug(__name__, f'Received command stats list {ctx}') + self._logger.debug(__name__, f'Received command stats add {ctx}: {name}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return if not self._permissions.is_member_technician(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command stats list') + self._logger.trace(__name__, f'Finished command stats add') return form = AddStatisticForm(stats, name, self._message_service, self._logger, self._t) - self._logger.trace(__name__, f'Finished stats command') + self._logger.trace(__name__, f'Finished stats add command') self._logger.trace(__name__, f'Started stats command form') await ctx.interaction.response.send_modal(form) + + @stats.command() + @commands.guild_only() + async def edit(self, ctx: Context, name: str): + self._logger.debug(__name__, f'Received command stats edit {ctx}: {name}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_technician(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command stats edit') + return + + try: + statistic = stats.where(lambda s: s.name == name).single() + form = AddStatisticForm(stats, name, self._message_service, self._logger, self._t, code=statistic.code, description=statistic.description) + self._logger.trace(__name__, f'Finished stats edit command') + self._logger.trace(__name__, f'Started stats command form') + await ctx.interaction.response.send_modal(form) + except Exception as e: + self._logger.error(__name__, f'Cannot view statistic {name}', e) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.edit.failed')) + + @edit.autocomplete('name') + async def edit_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] diff --git a/kdb-bot/src/modules/stats/model/statistic.py b/kdb-bot/src/modules/stats/model/statistic.py index d1b6558d..1ab0afe4 100644 --- a/kdb-bot/src/modules/stats/model/statistic.py +++ b/kdb-bot/src/modules/stats/model/statistic.py @@ -16,6 +16,14 @@ class Statistic: def description(self) -> str: return self._description + @description.setter + def description(self, value: str): + self._description = value + @property def code(self) -> str: return self._code + + @code.setter + def code(self, value: str): + self._code = value diff --git a/kdb-bot/src/modules/stats/ui/add_statistic_form.py b/kdb-bot/src/modules/stats/ui/add_statistic_form.py index 9591fb21..fe053241 100644 --- a/kdb-bot/src/modules/stats/ui/add_statistic_form.py +++ b/kdb-bot/src/modules/stats/ui/add_statistic_form.py @@ -20,6 +20,8 @@ class AddStatisticForm(ui.Modal): message_service: MessageServiceABC, logger: CommandLogger, t: TranslatePipe, + code: str = None, + description: str = None, ): ui.Modal.__init__(self, title=name) @@ -29,7 +31,28 @@ class AddStatisticForm(ui.Modal): self._logger = logger self._t = t + if code is not None: + self.code.default = code + + if description is not None: + self.description.default = description + async def on_submit(self, interaction: discord.Interaction): - self._stats.append(Statistic(self._name, self.description.value, self.code.value)) - await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.success')) + statistic = self._stats.where(lambda s: s.name == self._name).single_or_default() + try: + if statistic is None: + self._stats.append(Statistic(self._name, self.description.value, self.code.value)) + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.success')) + return + + statistic.description = self.description.value + statistic.code = self.code.value + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.edit.success')) + except Exception as e: + self._logger.error(__name__, f'Save statistic {self._name} failed', e) + if statistic is None: + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.failed')) + else: + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.edit.failed')) + self._logger.trace(__name__, f'Finished stats command form') From c9ce81701f383adabb6c8febaad2f4fefb555409 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 18:01:14 +0100 Subject: [PATCH 142/275] Added stats remove command #46 --- .../src/modules/stats/command/stats_group.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index 58105c0b..67c8469a 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -168,9 +168,32 @@ class StatsGroup(DiscordCommandABC): self._logger.trace(__name__, f'Started stats command form') await ctx.interaction.response.send_modal(form) except Exception as e: - self._logger.error(__name__, f'Cannot view statistic {name}', e) + self._logger.error(__name__, f'Cannot edit statistic {name}', e) await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.edit.failed')) @edit.autocomplete('name') async def edit_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] + + @stats.command() + @commands.guild_only() + async def remove(self, ctx: Context, name: str): + self._logger.debug(__name__, f'Received command stats remove {ctx}: {name}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_technician(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command stats remove') + return + + try: + stats.remove(stats.where(lambda s: s.name == name).single()) + self._logger.trace(__name__, f'Finished stats remove command') + except Exception as e: + self._logger.error(__name__, f'Cannot remove statistic {name}', e) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.edit.failed')) + + @remove.autocomplete('name') + async def edit_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] From fa7e41469b7291a98b353d220f22dc9e7bb0860d Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 18:22:27 +0100 Subject: [PATCH 143/275] Added stats migration #46 --- .../src/bot/startup_migration_extension.py | 2 ++ .../src/bot_data/migration/stats_migration.py | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 kdb-bot/src/bot_data/migration/stats_migration.py diff --git a/kdb-bot/src/bot/startup_migration_extension.py b/kdb-bot/src/bot/startup_migration_extension.py index a5d75b6d..724db39a 100644 --- a/kdb-bot/src/bot/startup_migration_extension.py +++ b/kdb-bot/src/bot/startup_migration_extension.py @@ -8,6 +8,7 @@ from bot_data.migration.api_migration import ApiMigration from bot_data.migration.auto_role_migration import AutoRoleMigration from bot_data.migration.initial_migration import InitialMigration from bot_data.migration.level_migration import LevelMigration +from bot_data.migration.stats_migration import StatsMigration from bot_data.service.migration_service import MigrationService @@ -25,3 +26,4 @@ class StartupMigrationExtension(StartupExtensionABC): services.add_transient(MigrationABC, AutoRoleMigration) # 03.10.2022 #54 - 0.2.2 services.add_transient(MigrationABC, ApiMigration) # 15.10.2022 #70 - 0.3.0 services.add_transient(MigrationABC, LevelMigration) # 06.11.2022 #25 - 0.3.0 + services.add_transient(MigrationABC, StatsMigration) # 09.11.2022 #46 - 0.3.0 diff --git a/kdb-bot/src/bot_data/migration/stats_migration.py b/kdb-bot/src/bot_data/migration/stats_migration.py new file mode 100644 index 00000000..92ba17ab --- /dev/null +++ b/kdb-bot/src/bot_data/migration/stats_migration.py @@ -0,0 +1,36 @@ +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.migration_abc import MigrationABC +from bot_data.db_context import DBContext + + +class StatsMigration(MigrationABC): + name = '0.3_StatsMigration' + + def __init__(self, logger: DatabaseLogger, db: DBContext): + MigrationABC.__init__(self) + self._logger = logger + self._db = db + self._cursor = db.cursor + + def upgrade(self): + self._logger.debug(__name__, 'Running upgrade') + + self._cursor.execute( + str(f""" + CREATE TABLE IF NOT EXISTS `Statistics` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `Name` VARCHAR(255) NOT NULL, + `Description` VARCHAR(255) NOT NULL, + `Code` LONGTEXT NOT NULL, + `ServerId` BIGINT, + `CreatedAt` DATETIME(6), + `LastModifiedAt` DATETIME(6), + PRIMARY KEY(`Id`), + FOREIGN KEY (`ServerId`) REFERENCES `Servers`(`ServerId`) + ); + """) + ) + + def downgrade(self): + self._cursor.execute('DROP TABLE `Statistics`;') + From a43676e9bfe691051a6b0ccc082e3baabd881f8d Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 18:36:48 +0100 Subject: [PATCH 144/275] Added stats table #46 --- kdb-bot/src/bot_data/model/statistic.py | 108 ++++++++++++++++++ .../src/modules/stats/command/stats_group.py | 2 +- kdb-bot/src/modules/stats/model/statistic.py | 29 ----- .../modules/stats/ui/add_statistic_form.py | 6 +- 4 files changed, 112 insertions(+), 33 deletions(-) create mode 100644 kdb-bot/src/bot_data/model/statistic.py delete mode 100644 kdb-bot/src/modules/stats/model/statistic.py diff --git a/kdb-bot/src/bot_data/model/statistic.py b/kdb-bot/src/bot_data/model/statistic.py new file mode 100644 index 00000000..1c34a264 --- /dev/null +++ b/kdb-bot/src/bot_data/model/statistic.py @@ -0,0 +1,108 @@ +from datetime import datetime +from typing import Optional + +from cpl_core.database import TableABC + +from bot_data.model.server import Server + + +class Statistic(TableABC): + + def __init__(self, name: str, description: str, code: str, server: Server, created_at: datetime=None, modified_at: datetime=None, id=0): + self._id = id + self._name = name + self._description = description + self._code = code + self._server = server + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def id(self) -> int: + return self._id + + @property + def name(self) -> str: + return self._name + + @property + def description(self) -> str: + return self._description + + @description.setter + def description(self, value: str): + self._description = value + + @property + def code(self) -> str: + return self._code + + @code.setter + def code(self, value: str): + self._code = value + + @property + def server(self) -> Server: + return self._server + + @staticmethod + def get_select_all_string() -> str: + return str(f""" + SELECT * FROM `Statistics`; + """) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `Statistics` + WHERE `Id` = {id}; + """) + + @staticmethod + def get_select_by_name_string(name: str, s_id: int) -> str: + return str(f""" + SELECT * FROM `Statistics` + WHERE `Name` = {name}; + """) + + @staticmethod + def get_select_by_server_string(s_id: int) -> str: + return str(f""" + SELECT * FROM `Statistics` + WHERE `ServerId` = {s_id}; + """) + + @property + def insert_string(self) -> str: + return str(f""" + INSERT INTO `Statistics` ( + `Name`, `Description`, `Code`, `ServerId`, `CreatedAt`, `LastModifiedAt` + ) VALUES ( + '{self._name}', + '{self._description}', + '{self._code}', + {self._server.server_id}, + '{self._created_at}', + '{self._modified_at}' + ); + """) + + @property + def udpate_string(self) -> str: + return str(f""" + UPDATE `Statistics` + SET `Name` = {self._name}, + `Description` = '{self._description}' + `Code` = '{self._code}' + `LastModifiedAt` = '{self._modified_at}' + WHERE `Id` = {self._id}; + """) + + @property + def delete_string(self) -> str: + return str(f""" + DELETE FROM `Statistics` + WHERE `Id` = {self._id}; + """) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index 67c8469a..0d61f249 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -13,8 +13,8 @@ from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.logging.command_logger import CommandLogger from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.model.statistic import Statistic from modules.permission.abc.permission_service_abc import PermissionServiceABC -from modules.stats.model.statistic import Statistic from modules.stats.service.statistic_service import StatisticService from modules.stats.ui.add_statistic_form import AddStatisticForm diff --git a/kdb-bot/src/modules/stats/model/statistic.py b/kdb-bot/src/modules/stats/model/statistic.py deleted file mode 100644 index 1ab0afe4..00000000 --- a/kdb-bot/src/modules/stats/model/statistic.py +++ /dev/null @@ -1,29 +0,0 @@ -from typing import Callable - - -class Statistic: - - def __init__(self, name: str, description: str, code: str): - self._name = name - self._description = description - self._code = code - - @property - def name(self) -> str: - return self._name - - @property - def description(self) -> str: - return self._description - - @description.setter - def description(self, value: str): - self._description = value - - @property - def code(self) -> str: - return self._code - - @code.setter - def code(self, value: str): - self._code = value diff --git a/kdb-bot/src/modules/stats/ui/add_statistic_form.py b/kdb-bot/src/modules/stats/ui/add_statistic_form.py index fe053241..073fc9d4 100644 --- a/kdb-bot/src/modules/stats/ui/add_statistic_form.py +++ b/kdb-bot/src/modules/stats/ui/add_statistic_form.py @@ -5,7 +5,7 @@ from discord import ui, TextStyle from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.logging.command_logger import CommandLogger -from modules.stats.model.statistic import Statistic +from modules.stats.model.statisticmodel import StatisticModel class AddStatisticForm(ui.Modal): @@ -15,7 +15,7 @@ class AddStatisticForm(ui.Modal): def __init__( self, - stats: List[Statistic], + stats: List[StatisticModel], name: str, message_service: MessageServiceABC, logger: CommandLogger, @@ -41,7 +41,7 @@ class AddStatisticForm(ui.Modal): statistic = self._stats.where(lambda s: s.name == self._name).single_or_default() try: if statistic is None: - self._stats.append(Statistic(self._name, self.description.value, self.code.value)) + self._stats.append(StatisticModel(self._name, self.description.value, self.code.value)) await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.success')) return From c7967c6904928c80d451ac8e248837e3325c17b1 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 18:49:11 +0100 Subject: [PATCH 145/275] Added stats repo #46 --- .../bot_data/abc/statistic_repository_abc.py | 36 ++++++++ .../service/statistic_repository_service.py | 88 +++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 kdb-bot/src/bot_data/abc/statistic_repository_abc.py create mode 100644 kdb-bot/src/bot_data/service/statistic_repository_service.py diff --git a/kdb-bot/src/bot_data/abc/statistic_repository_abc.py b/kdb-bot/src/bot_data/abc/statistic_repository_abc.py new file mode 100644 index 00000000..bf16055d --- /dev/null +++ b/kdb-bot/src/bot_data/abc/statistic_repository_abc.py @@ -0,0 +1,36 @@ +from abc import ABC, abstractmethod +from typing import Optional + +from cpl_query.extension import List + +from bot_data.model.statistic import Statistic + + +class StatisticRepositoryABC(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + def get_statistics(self) -> List[Statistic]: pass + + @abstractmethod + def get_statistics_by_server_id(self, server_id: int) -> List[Statistic]: pass + + @abstractmethod + def get_statistic_by_id(self, id: int) -> Statistic: pass + + @abstractmethod + def get_statistic_by_name(self, name: str, server_id: int) -> Statistic: pass + + @abstractmethod + def find_statistic_by_name(self, name: str, server_id: int) -> Optional[Statistic]: pass + + @abstractmethod + def add_statistic(self, statistic: Statistic): pass + + @abstractmethod + def update_statistic(self, statistic: Statistic): pass + + @abstractmethod + def delete_statistic(self, statistic: Statistic): pass diff --git a/kdb-bot/src/bot_data/service/statistic_repository_service.py b/kdb-bot/src/bot_data/service/statistic_repository_service.py new file mode 100644 index 00000000..222bdf38 --- /dev/null +++ b/kdb-bot/src/bot_data/service/statistic_repository_service.py @@ -0,0 +1,88 @@ +from typing import Optional + +from cpl_core.database.context import DatabaseContextABC +from cpl_query.extension import List + +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.statistic_repository_abc import StatisticRepositoryABC +from bot_data.model.statistic import Statistic + + +class StatisticRepositoryService(StatisticRepositoryABC): + + def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC, statistics: ServerRepositoryABC): + self._logger = logger + self._context = db_context + + self._statistics = statistics + + StatisticRepositoryABC.__init__(self) + + @staticmethod + def _get_value_from_result(value: any) -> Optional[any]: + if isinstance(value, str) and 'NULL' in value: + return None + + return value + + def _statistic_from_result(self, sql_result: tuple) -> Statistic: + statistic = Statistic( + self._get_value_from_result(sql_result[1]), + self._get_value_from_result(sql_result[2]), + self._get_value_from_result(sql_result[3]), + self._statistics.get_server_by_id(sql_result[4]), + id=self._get_value_from_result(sql_result[0]) + ) + + return statistic + + def get_statistics(self) -> List[Statistic]: + statistics = List(Statistic) + self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_all_string()}') + results = self._context.select(Statistic.get_select_all_string()) + for result in results: + statistics.append(self._statistic_from_result(result)) + + return statistics + + def get_statistics_by_server_id(self, server_id: int) -> List[Statistic]: + statistics = List(Statistic) + self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_by_server_string(server_id)}') + results = self._context.select(Statistic.get_select_by_server_string(server_id)) + for result in results: + statistics.append(self._statistic_from_result(result)) + + return statistics + + def get_statistic_by_id(self, id: int) -> Statistic: + self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_by_id_string(id)}') + result = self._context.select(Statistic.get_select_by_id_string(id))[0] + return self._statistic_from_result(result) + + def get_statistic_by_name(self, name: str, server_id: int) -> Statistic: + self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_by_name_string(name, server_id)}') + result = self._context.select(Statistic.get_select_by_name_string(name, server_id))[0] + return self._statistic_from_result(result) + + def find_statistic_by_name(self, name: str, server_id: int) -> Optional[Statistic]: + self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_by_name_string(name, server_id)}') + result = self._context.select(Statistic.get_select_by_name_string(name, server_id)) + if result is None or len(result) == 0: + return None + + result = result[0] + + return self._statistic_from_result(result) + + def add_statistic(self, statistic: Statistic): + self._logger.trace(__name__, f'Send SQL command: {statistic.insert_string}') + self._context.cursor.execute(statistic.insert_string) + + def update_statistic(self, statistic: Statistic): + self._logger.trace(__name__, f'Send SQL command: {statistic.udpate_string}') + self._context.cursor.execute(statistic.udpate_string) + + def delete_statistic(self, statistic: Statistic): + self._logger.trace(__name__, f'Send SQL command: {statistic.delete_string}') + self._context.cursor.execute(statistic.delete_string) From bd7983189267ab75602cc4b45decdaeb437bf43b Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 19:23:10 +0100 Subject: [PATCH 146/275] Added db to stats #46 --- kdb-bot/src/bot/translation/de.json | 7 +- kdb-bot/src/bot_data/data_module.py | 3 + kdb-bot/src/bot_data/model/statistic.py | 17 ++--- .../service/statistic_repository_service.py | 7 +- .../src/modules/stats/command/stats_group.py | 66 +++++++++++-------- .../modules/stats/ui/add_statistic_form.py | 26 ++++++-- 6 files changed, 85 insertions(+), 41 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 0768197a..58bb23d4 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -199,7 +199,8 @@ "stats": { "list": { "statistic": "Statistik", - "description": "Beschreibung" + "description": "Beschreibung", + "nothing_found": "Keine Statistiken gefunden." }, "view": { "statistic": "Statistik", @@ -213,6 +214,10 @@ "edit": { "failed": "Statistik kann nicht bearbeitet werden :(", "success": "Statistik wurde gespeichert :D" + }, + "remove": { + "failed": "Statistik kann nicht gelöscht werden :(", + "success": "Statistik wurde gelöscht :D" } } }, diff --git a/kdb-bot/src/bot_data/data_module.py b/kdb-bot/src/bot_data/data_module.py index 0668bffe..e32d8039 100644 --- a/kdb-bot/src/bot_data/data_module.py +++ b/kdb-bot/src/bot_data/data_module.py @@ -11,6 +11,7 @@ from bot_data.abc.client_repository_abc import ClientRepositoryABC from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC from bot_data.abc.level_repository_abc import LevelRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.statistic_repository_abc import StatisticRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC @@ -21,6 +22,7 @@ from bot_data.service.known_user_repository_service import KnownUserRepositorySe from bot_data.service.level_repository_service import LevelRepositoryService from bot_data.service.seeder_service import SeederService from bot_data.service.server_repository_service import ServerRepositoryService +from bot_data.service.statistic_repository_service import StatisticRepositoryService from bot_data.service.user_joined_server_repository_service import UserJoinedServerRepositoryService from bot_data.service.user_joined_voice_channel_service import UserJoinedVoiceChannelRepositoryService from bot_data.service.user_repository_service import UserRepositoryService @@ -44,5 +46,6 @@ class DataModule(ModuleABC): services.add_transient(UserJoinedVoiceChannelRepositoryABC, UserJoinedVoiceChannelRepositoryService) services.add_transient(AutoRoleRepositoryABC, AutoRoleRepositoryService) services.add_transient(LevelRepositoryABC, LevelRepositoryService) + services.add_transient(StatisticRepositoryABC, StatisticRepositoryService) services.add_transient(SeederService) diff --git a/kdb-bot/src/bot_data/model/statistic.py b/kdb-bot/src/bot_data/model/statistic.py index 1c34a264..11011237 100644 --- a/kdb-bot/src/bot_data/model/statistic.py +++ b/kdb-bot/src/bot_data/model/statistic.py @@ -1,7 +1,7 @@ from datetime import datetime -from typing import Optional from cpl_core.database import TableABC +from cpl_core.utils import CredentialManager from bot_data.model.server import Server @@ -12,7 +12,7 @@ class Statistic(TableABC): self._id = id self._name = name self._description = description - self._code = code + self._code = CredentialManager.encrypt(code) self._server = server TableABC.__init__(self) @@ -37,11 +37,11 @@ class Statistic(TableABC): @property def code(self) -> str: - return self._code + return CredentialManager.decrypt(self._code) @code.setter def code(self, value: str): - self._code = value + self._code = CredentialManager.encrypt(value) @property def server(self) -> Server: @@ -64,7 +64,8 @@ class Statistic(TableABC): def get_select_by_name_string(name: str, s_id: int) -> str: return str(f""" SELECT * FROM `Statistics` - WHERE `Name` = {name}; + WHERE `ServerId` = {s_id} + AND `Name` = '{name}'; """) @staticmethod @@ -93,9 +94,9 @@ class Statistic(TableABC): def udpate_string(self) -> str: return str(f""" UPDATE `Statistics` - SET `Name` = {self._name}, - `Description` = '{self._description}' - `Code` = '{self._code}' + SET `Name` = '{self._name}', + `Description` = '{self._description}', + `Code` = '{self._code}', `LastModifiedAt` = '{self._modified_at}' WHERE `Id` = {self._id}; """) diff --git a/kdb-bot/src/bot_data/service/statistic_repository_service.py b/kdb-bot/src/bot_data/service/statistic_repository_service.py index 222bdf38..8f31dbe2 100644 --- a/kdb-bot/src/bot_data/service/statistic_repository_service.py +++ b/kdb-bot/src/bot_data/service/statistic_repository_service.py @@ -1,6 +1,7 @@ from typing import Optional from cpl_core.database.context import DatabaseContextABC +from cpl_core.utils import CredentialManager from cpl_query.extension import List from bot_core.logging.database_logger import DatabaseLogger @@ -27,10 +28,14 @@ class StatisticRepositoryService(StatisticRepositoryABC): return value def _statistic_from_result(self, sql_result: tuple) -> Statistic: + code = self._get_value_from_result(sql_result[3]) + if code is not None: + code = CredentialManager.decrypt(code) + statistic = Statistic( self._get_value_from_result(sql_result[1]), self._get_value_from_result(sql_result[2]), - self._get_value_from_result(sql_result[3]), + code, self._statistics.get_server_by_id(sql_result[4]), id=self._get_value_from_result(sql_result[0]) ) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index 0d61f249..96734119 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -1,9 +1,8 @@ -import textwrap from typing import List as TList import discord +from cpl_core.database.context import DatabaseContextABC from cpl_discord.command import DiscordCommandABC -from cpl_query.extension import List from cpl_translation import TranslatePipe from discord import app_commands from discord.ext import commands @@ -13,29 +12,11 @@ from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.logging.command_logger import CommandLogger from bot_data.abc.server_repository_abc import ServerRepositoryABC -from bot_data.model.statistic import Statistic +from bot_data.abc.statistic_repository_abc import StatisticRepositoryABC from modules.permission.abc.permission_service_abc import PermissionServiceABC from modules.stats.service.statistic_service import StatisticService from modules.stats.ui.add_statistic_form import AddStatisticForm -stats = List(Statistic, [ - Statistic( - 'Benutzer XP Aufsteigend', - 'Zeigt XP von jedem Benutzer, aufsteigend nach XP', - textwrap.dedent("""\ - result.header.append('Name') - result.header.append('XP') - - for user in users.order_by(lambda u: u.xp): - row = List(str) - member = guild.get_member(user.discord_id) - row.append(member.name) - row.append(str(user.xp)) - result.values.append(row) - """)), - Statistic('Benutzer XP Absteigend', 'Zeigt XP von jedem Benutzer, absteigend nach XP', '') -]) - class StatsGroup(DiscordCommandABC): @@ -48,6 +29,8 @@ class StatsGroup(DiscordCommandABC): permission_service: PermissionServiceABC, statistic: StatisticService, servers: ServerRepositoryABC, + stats: StatisticRepositoryABC, + db: DatabaseContextABC, ): DiscordCommandABC.__init__(self) @@ -58,6 +41,8 @@ class StatsGroup(DiscordCommandABC): self._permissions = permission_service self._statistic = statistic self._servers = servers + self._stats = stats + self._db = db @commands.hybrid_group() @commands.guild_only() @@ -76,17 +61,27 @@ class StatsGroup(DiscordCommandABC): self._logger.trace(__name__, f'Finished command stats list') return + if ctx.guild is None: + return + embed = discord.Embed( title=self._t.transform('modules.auto_role.list.title'), description=self._t.transform('modules.auto_role.list.description'), color=int('ef9d0d', 16) ) + server = self._servers.get_server_by_discord_id(ctx.guild.id) + stats = self._stats.get_statistics_by_server_id(server.server_id) + + if stats.count() == 0: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.list.nothing_found')) + return + statistics = '' descriptions = '' for statistic in stats: - statistics += f'\n{statistic["Name"]}' - descriptions += f'\n{statistic["Description"]}' + statistics += f'\n{statistic.name}' + descriptions += f'\n{statistic.description}' embed.add_field(name=self._t.transform('modules.stats.list.statistic'), value=statistics, inline=True) embed.add_field(name=self._t.transform('modules.stats.list.description'), value=statistics, inline=True) @@ -105,6 +100,7 @@ class StatsGroup(DiscordCommandABC): try: server = self._servers.get_server_by_discord_id(ctx.guild.id) + stats = self._stats.get_statistics_by_server_id(server.server_id) statistic = stats.where(lambda s: s.name == name).single() result = await self._statistic.execute(statistic.code, server) @@ -130,6 +126,8 @@ class StatsGroup(DiscordCommandABC): @view.autocomplete('name') async def view_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + server = self._servers.get_server_by_discord_id(interaction.guild.id) + stats = self._stats.get_statistics_by_server_id(server.server_id) return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] @stats.command() @@ -144,7 +142,11 @@ class StatsGroup(DiscordCommandABC): self._logger.trace(__name__, f'Finished command stats add') return - form = AddStatisticForm(stats, name, self._message_service, self._logger, self._t) + if ctx.guild is None: + return + + server = self._servers.get_server_by_discord_id(ctx.guild.id) + form = AddStatisticForm(server, self._stats, self._db, name, self._message_service, self._logger, self._t) self._logger.trace(__name__, f'Finished stats add command') self._logger.trace(__name__, f'Started stats command form') await ctx.interaction.response.send_modal(form) @@ -162,8 +164,10 @@ class StatsGroup(DiscordCommandABC): return try: + server = self._servers.get_server_by_discord_id(ctx.guild.id) + stats = self._stats.get_statistics_by_server_id(server.server_id) statistic = stats.where(lambda s: s.name == name).single() - form = AddStatisticForm(stats, name, self._message_service, self._logger, self._t, code=statistic.code, description=statistic.description) + form = AddStatisticForm(server, self._stats, self._db, name, self._message_service, self._logger, self._t, code=statistic.code, description=statistic.description) self._logger.trace(__name__, f'Finished stats edit command') self._logger.trace(__name__, f'Started stats command form') await ctx.interaction.response.send_modal(form) @@ -173,6 +177,8 @@ class StatsGroup(DiscordCommandABC): @edit.autocomplete('name') async def edit_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + server = self._servers.get_server_by_discord_id(interaction.guild.id) + stats = self._stats.get_statistics_by_server_id(server.server_id) return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] @stats.command() @@ -188,12 +194,18 @@ class StatsGroup(DiscordCommandABC): return try: - stats.remove(stats.where(lambda s: s.name == name).single()) + server = self._servers.get_server_by_discord_id(ctx.guild.id) + statistic = self._stats.get_statistic_by_name(name, server.server_id) + self._stats.delete_statistic(statistic) + self._db.save_changes() + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.remove.success')) self._logger.trace(__name__, f'Finished stats remove command') except Exception as e: self._logger.error(__name__, f'Cannot remove statistic {name}', e) - await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.edit.failed')) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.remove.failed')) @remove.autocomplete('name') async def edit_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + server = self._servers.get_server_by_discord_id(interaction.guild.id) + stats = self._stats.get_statistics_by_server_id(server.server_id) return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] diff --git a/kdb-bot/src/modules/stats/ui/add_statistic_form.py b/kdb-bot/src/modules/stats/ui/add_statistic_form.py index 073fc9d4..625bac82 100644 --- a/kdb-bot/src/modules/stats/ui/add_statistic_form.py +++ b/kdb-bot/src/modules/stats/ui/add_statistic_form.py @@ -1,11 +1,14 @@ import discord +from cpl_core.database.context import DatabaseContextABC from cpl_query.extension import List from cpl_translation import TranslatePipe from discord import ui, TextStyle from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.logging.command_logger import CommandLogger -from modules.stats.model.statisticmodel import StatisticModel +from bot_data.abc.statistic_repository_abc import StatisticRepositoryABC +from bot_data.model.server import Server +from bot_data.model.statistic import Statistic class AddStatisticForm(ui.Modal): @@ -15,7 +18,9 @@ class AddStatisticForm(ui.Modal): def __init__( self, - stats: List[StatisticModel], + server: Server, + stats: StatisticRepositoryABC, + db: DatabaseContextABC, name: str, message_service: MessageServiceABC, logger: CommandLogger, @@ -25,7 +30,9 @@ class AddStatisticForm(ui.Modal): ): ui.Modal.__init__(self, title=name) + self._server = server self._stats = stats + self._db = db self._name = name self._message_service = message_service self._logger = logger @@ -38,15 +45,26 @@ class AddStatisticForm(ui.Modal): self.description.default = description async def on_submit(self, interaction: discord.Interaction): - statistic = self._stats.where(lambda s: s.name == self._name).single_or_default() + statistic = self._stats.get_statistics_by_server_id(self._server.server_id).where(lambda s: s.name == self._name).single_or_default() + + if interaction.guild is None: + if statistic is None: + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.failed')) + else: + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.edit.failed')) + return + try: if statistic is None: - self._stats.append(StatisticModel(self._name, self.description.value, self.code.value)) + self._stats.add_statistic(Statistic(self._name, self.description.value, self.code.value, self._server)) + self._db.save_changes() await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.success')) return statistic.description = self.description.value statistic.code = self.code.value + self._stats.update_statistic(statistic) + self._db.save_changes() await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.edit.success')) except Exception as e: self._logger.error(__name__, f'Save statistic {self._name} failed', e) From 455c1e7023c6b3efe0a715a389c115c81b271ef5 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 19:26:41 +0100 Subject: [PATCH 147/275] Fixed translation #26 --- kdb-bot/src/bot/translation/de.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index ea8eb111..ecc7359a 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -193,6 +193,12 @@ "seeding_failed": "Levelsystem konnte nicht neu geladen werden.", "seeding_finished": "Levelsystem wurde Erfolgreich neu geladen." }, + "remove": { + "success": "Level {} wurde entfernt :D", + "error": { + "not_found": "Level {} nicht gefunden!" + } + }, "down": { "already_first": "{} hat bereits das erste Level.", "success": "{} wurde auf Level {} runtergesetzt :)", From c632ad51d4e2a614e1187a03f6ccaa822f87f741 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 19:33:45 +0100 Subject: [PATCH 148/275] Fixed stuff caused by merges #26 --- kdb-bot/src/bot/translation/de.json | 10 +++++----- .../src/modules/level/command/level_group.py | 20 ++++++++++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index ecc7359a..0aa1395e 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -62,7 +62,7 @@ "gold": "Gold", "green": "Grün", "greyple": "Graugrün", - "light_gray": "Hellgrau", + "light_grey": "Hellgrau", "magenta": "Magenta", "orange": "Orange", "purple": "Violett", @@ -175,6 +175,9 @@ }, "level": { "new_level_message": "<@{}> ist nun Level {}", + "seeding_started": "Levelsystem wird neu geladen.", + "seeding_failed": "Levelsystem konnte nicht neu geladen werden.", + "seeding_finished": "Levelsystem wurde Erfolgreich neu geladen.", "error": { "nothing_found": "Keine Level Einträge gefunden.", "level_with_name_already_exists": "Ein Level mit dem Namen {} existiert bereits!", @@ -188,10 +191,7 @@ "permission_int": "Berechtigungen" }, "create": { - "created": "Level {} mit Berechtigungen {} wurde erstellt.", - "seeding_started": "Levelsystem wird neu geladen.", - "seeding_failed": "Levelsystem konnte nicht neu geladen werden.", - "seeding_finished": "Levelsystem wurde Erfolgreich neu geladen." + "created": "Level {} mit Berechtigungen {} wurde erstellt." }, "remove": { "success": "Level {} wurde entfernt :D", diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index da59dbb6..c024dbe3 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -57,6 +57,16 @@ class LevelGroup(DiscordCommandABC): self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') + async def _seed_levels(self, channel: discord.TextChannel): + # send message to ctx.channel because send_ctx_msg resolves ctx + try: + await self._message_service.send_channel_message(channel, self._t.transform('modules.level.seeding_started')) + await self._level_seeder.seed() + await self._message_service.send_channel_message(channel, self._t.transform('modules.level.seeding_finished')) + except Exception as e: + self._logger.error(__name__, f'Level seeding failed', e) + await self._message_service.send_channel_message(channel, self._t.transform('modules.level.seeding_failed')) + @commands.hybrid_group() @commands.guild_only() async def level(self, ctx: Context): @@ -146,15 +156,7 @@ class LevelGroup(DiscordCommandABC): self._logger.error(__name__, f'Could not save level {name} with color {color}, min_xp {min_xp} and permissions {permissions}', e) else: await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.create.created').format(name, permissions)) - - # send message to ctx.channel because send_ctx_msg resolves ctx - try: - await self._message_service.send_channel_message(ctx.channel, self._t.transform('modules.level.create.seeding_started')) - await self._level_seeder.seed() - await self._message_service.send_channel_message(ctx.channel, self._t.transform('modules.level.create.seeding_finished')) - except Exception as e: - self._logger.error(__name__, f'Level seeding failed', e) - await self._message_service.send_channel_message(ctx.channel, self._t.transform('modules.level.create.seeding_failed')) + await self._seed_levels(ctx.channel) self._logger.trace(__name__, f'Finished command level create') From a5ef2551e8c7521589d153ab5e5d540d20d68aa2 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 19:50:57 +0100 Subject: [PATCH 149/275] Improved error translation #26 --- kdb-bot/src/bot/translation/de.json | 3 ++- kdb-bot/src/modules/level/command/level_group.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 0aa1395e..45281393 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -212,7 +212,8 @@ "set": { "already_level": "{} hat bereits das Level {} :/", "success": "{} ist nun Level {} :)", - "failed": "Das Level von {} konnte nicht auf {} gesetzt werden :(" + "failed": "Das Level von {} konnte nicht auf {} gesetzt werden :(", + "not_found": "Das Level {} konnte nicht gefunden werden :(" } }, "database": {}, diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index c024dbe3..32ccb708 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -343,7 +343,7 @@ class LevelGroup(DiscordCommandABC): new_level = self._levels.get_levels_by_server_id(server.server_id).where(lambda l: l.name == level).single_or_default() if new_level is None: - await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.set.failed').format(member.name, level)) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.set.not_found').format(level)) self._logger.trace(__name__, f'Finished command level set') return From c6a5d49942747ca77992d872c84661b03782e04b Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 20:28:38 +0100 Subject: [PATCH 150/275] Added logic to call statistics #46 --- kdb-bot/src/bot/translation/de.json | 12 +- kdb-bot/src/modules/stats/__init__.py | 1 + kdb-bot/src/modules/stats/command/__init__.py | 1 + .../src/modules/stats/command/stats_group.py | 133 ++++++++++++++++++ kdb-bot/src/modules/stats/model/__init__.py | 1 + kdb-bot/src/modules/stats/model/statistic.py | 21 +++ .../modules/stats/model/statistic_result.py | 24 ++++ kdb-bot/src/modules/stats/service/__init__.py | 1 + .../stats/service/statistic_service.py | 69 +++++++++ kdb-bot/src/modules/stats/stats.json | 46 ++++++ kdb-bot/src/modules/stats/test/user_xp_asc.py | 37 +++++ .../src/modules/stats/test/user_xp_desc.py | 37 +++++ 12 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 kdb-bot/src/modules/stats/__init__.py create mode 100644 kdb-bot/src/modules/stats/command/__init__.py create mode 100644 kdb-bot/src/modules/stats/command/stats_group.py create mode 100644 kdb-bot/src/modules/stats/model/__init__.py create mode 100644 kdb-bot/src/modules/stats/model/statistic.py create mode 100644 kdb-bot/src/modules/stats/model/statistic_result.py create mode 100644 kdb-bot/src/modules/stats/service/__init__.py create mode 100644 kdb-bot/src/modules/stats/service/statistic_service.py create mode 100644 kdb-bot/src/modules/stats/stats.json create mode 100644 kdb-bot/src/modules/stats/test/user_xp_asc.py create mode 100644 kdb-bot/src/modules/stats/test/user_xp_desc.py diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 45281393..edb2e87c 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -217,7 +217,17 @@ } }, "database": {}, - "permission": { + "permission": {}, + "stats": { + "list": { + "statistic": "Statistik", + "description": "Beschreibung" + }, + "view": { + "statistic": "Statistik", + "description": "Beschreibung", + "failed": "Statistik kann nicht gezeigt werden :(" + } } }, "api": { diff --git a/kdb-bot/src/modules/stats/__init__.py b/kdb-bot/src/modules/stats/__init__.py new file mode 100644 index 00000000..ad5eca30 --- /dev/null +++ b/kdb-bot/src/modules/stats/__init__.py @@ -0,0 +1 @@ +# imports: diff --git a/kdb-bot/src/modules/stats/command/__init__.py b/kdb-bot/src/modules/stats/command/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/kdb-bot/src/modules/stats/command/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py new file mode 100644 index 00000000..32b32f61 --- /dev/null +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -0,0 +1,133 @@ +from typing import List as TList + +import discord +from cpl_discord.command import DiscordCommandABC +from cpl_query.extension import List +from cpl_translation import TranslatePipe +from discord import app_commands +from discord.ext import commands +from discord.ext.commands import Context + +from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC +from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.logging.command_logger import CommandLogger +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from modules.permission.abc.permission_service_abc import PermissionServiceABC +from modules.stats.model.statistic import Statistic +from modules.stats.service.statistic_service import StatisticService +from modules.stats.test.user_xp_asc import user_xp_asc +from modules.stats.test.user_xp_desc import user_xp_desc + + +class StatsGroup(DiscordCommandABC): + + def __init__( + self, + logger: CommandLogger, + message_service: MessageServiceABC, + client_utils: ClientUtilsServiceABC, + translate: TranslatePipe, + permission_service: PermissionServiceABC, + statistic: StatisticService, + servers: ServerRepositoryABC, + ): + DiscordCommandABC.__init__(self) + + self._logger = logger + self._client_utils = client_utils + self._message_service = message_service + self._t = translate + self._permissions = permission_service + self._statistic = statistic + self._servers = servers + + self._stats = List(Statistic, [ + Statistic('Benutzer XP Aufsteigend', 'Zeigt XP von jedem Benutzer, aufsteigend nach XP', user_xp_asc), + Statistic('Benutzer XP Absteigend', 'Zeigt XP von jedem Benutzer, absteigend nach XP', user_xp_desc) + ]) + + @commands.hybrid_group() + @commands.guild_only() + async def stats(self, ctx: Context): + pass + + @stats.command(alias='rules') + @commands.guild_only() + async def list(self, ctx: Context, wait: int = None): + self._logger.debug(__name__, f'Received command stats list {ctx}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_moderator(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command stats list') + return + + embed = discord.Embed( + title=self._t.transform('modules.auto_role.list.title'), + description=self._t.transform('modules.auto_role.list.description'), + color=int('ef9d0d', 16) + ) + + statistics = '' + descriptions = '' + for statistic in self._stats: + statistics += f'\n{statistic["Name"]}' + descriptions += f'\n{statistic["Description"]}' + + embed.add_field(name=self._t.transform('modules.stats.list.statistic'), value=statistics, inline=True) + embed.add_field(name=self._t.transform('modules.stats.list.description'), value=statistics, inline=True) + await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait) + self._logger.trace(__name__, f'Finished command stats list') + + @stats.command() + @commands.guild_only() + async def view(self, ctx: Context, name: str, wait: int = None): + self._logger.debug(__name__, f'Received command stats {ctx}:{name}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if ctx.guild is None: + return + + try: + server = self._servers.get_server_by_discord_id(ctx.guild.id) + statistic = self._stats.where(lambda s: s.name == name).single() + result = await self._statistic.execute(statistic.func, server) + + # headers = '' + # rows = '' + # for header in result.header: + # headers += f'\n{header}' + # + # for row in result.values: + # row_str = '' + # for column in row: + # row_str += f'\n{column}' + # rows += f'\n{row_str}' + + embed = discord.Embed( + title=statistic.name, + description=statistic.description, + color=int('ef9d0d', 16) + ) + + for i in range(result.header.count()): + header = result.header[i] + value = '' + for row in result.values: + value += f'\n{row[i]}' + embed.add_field(name=header, value=value, inline=True) + + # embed.add_field(name=self._t.transform('modules.auto_role.list.message_id'), value=rows, inline=True) + await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait) + # await self._message_service.send_ctx_msg(ctx, name, wait_before_delete=wait) + except Exception as e: + self._logger.error(__name__, f'Cannot view statistic {name}', e) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.view.failed')) + + self._logger.trace(__name__, f'Finished stats command') + + @view.autocomplete('name') + async def view_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in self._stats] diff --git a/kdb-bot/src/modules/stats/model/__init__.py b/kdb-bot/src/modules/stats/model/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/kdb-bot/src/modules/stats/model/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/stats/model/statistic.py b/kdb-bot/src/modules/stats/model/statistic.py new file mode 100644 index 00000000..2db1b75d --- /dev/null +++ b/kdb-bot/src/modules/stats/model/statistic.py @@ -0,0 +1,21 @@ +from typing import Callable + + +class Statistic: + + def __init__(self, name: str, description: str, func: Callable): + self._name = name + self._description = description + self._func = func + + @property + def name(self) -> str: + return self._name + + @property + def description(self) -> str: + return self._description + + @property + def func(self) -> Callable: + return self._func diff --git a/kdb-bot/src/modules/stats/model/statistic_result.py b/kdb-bot/src/modules/stats/model/statistic_result.py new file mode 100644 index 00000000..c2c53742 --- /dev/null +++ b/kdb-bot/src/modules/stats/model/statistic_result.py @@ -0,0 +1,24 @@ +from cpl_query.extension import List + + +class StatisticResult: + + def __init__(self): + self._header = List(str) + self._values = List(List) + + @property + def header(self) -> List[str]: + return self._header + + @header.setter + def header(self, value: List[str]): + self._header = value + + @property + def values(self) -> List[List]: + return self._values + + @values.setter + def values(self, value: List[List]): + self._values = value diff --git a/kdb-bot/src/modules/stats/service/__init__.py b/kdb-bot/src/modules/stats/service/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/kdb-bot/src/modules/stats/service/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/stats/service/statistic_service.py b/kdb-bot/src/modules/stats/service/statistic_service.py new file mode 100644 index 00000000..b0f59d62 --- /dev/null +++ b/kdb-bot/src/modules/stats/service/statistic_service.py @@ -0,0 +1,69 @@ +from abc import abstractmethod +from typing import Callable + +from cpl_discord.service import DiscordBotServiceABC + +from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC +from bot_data.abc.client_repository_abc import ClientRepositoryABC +from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC +from bot_data.abc.level_repository_abc import LevelRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC +from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.server import Server +from modules.stats.model.statistic_result import StatisticResult + + +class StatisticService: + + def __init__( + self, + auto_roles: AutoRoleRepositoryABC, + clients: ClientRepositoryABC, + known_users: KnownUserRepositoryABC, + levels: LevelRepositoryABC, + servers: ServerRepositoryABC, + user_joined_servers: UserJoinedServerRepositoryABC, + user_joined_voice_channel: UserJoinedVoiceChannelRepositoryABC, + users: UserRepositoryABC, + bot: DiscordBotServiceABC, + ): + self._auto_roles = auto_roles + self._clients = clients + self._known_users = known_users + self._levels = levels + self._servers = servers + self._user_joined_servers = user_joined_servers + self._user_joined_voice_channel = user_joined_voice_channel + self._users = users + self._bot = bot + + async def execute(self, _f: Callable, server: Server) -> StatisticResult: + guild = self._bot.guilds.where(lambda g: g.id == server.discord_server_id).single() + + return await _f( + self._auto_roles + .get_auto_roles() + .where(lambda x: x.server.server_id == server.server_id), + self._clients + .get_clients() + .where(lambda x: x.server.server_id == server.server_id), + self._known_users.get_users(), + self._levels + .get_levels() + .where(lambda x: x.server.server_id == server.server_id), + self._servers + .get_servers() + .where(lambda x: x.server_id == server.server_id), + self._user_joined_servers + .get_user_joined_servers() + .where(lambda x: x.user.server.server_id == server.server_id), + self._user_joined_voice_channel + .get_user_joined_voice_channels() + .where(lambda x: x.user.server.server_id == server.server_id), + self._users + .get_users() + .where(lambda x: x.server.server_id == server.server_id), + guild + ) diff --git a/kdb-bot/src/modules/stats/stats.json b/kdb-bot/src/modules/stats/stats.json new file mode 100644 index 00000000..c4fec974 --- /dev/null +++ b/kdb-bot/src/modules/stats/stats.json @@ -0,0 +1,46 @@ +{ + "ProjectSettings": { + "Name": "stats", + "Version": { + "Major": "0", + "Minor": "0", + "Micro": "0" + }, + "Author": "", + "AuthorEmail": "", + "Description": "", + "LongDescription": "", + "URL": "", + "CopyrightDate": "", + "CopyrightName": "", + "LicenseName": "", + "LicenseDescription": "", + "Dependencies": [ + "cpl-core>=2022.10.0.post7" + ], + "DevDependencies": [ + "cpl-cli>=2022.10.1" + ], + "PythonVersion": ">=3.10.4", + "PythonPath": { + "linux": "" + }, + "Classifiers": [] + }, + "BuildSettings": { + "ProjectType": "library", + "SourcePath": "", + "OutputPath": "../../dist", + "Main": "stats.main", + "EntryPoint": "stats", + "IncludePackageData": false, + "Included": [], + "Excluded": [ + "*/__pycache__", + "*/logs", + "*/tests" + ], + "PackageData": {}, + "ProjectReferences": [] + } +} \ No newline at end of file diff --git a/kdb-bot/src/modules/stats/test/user_xp_asc.py b/kdb-bot/src/modules/stats/test/user_xp_asc.py new file mode 100644 index 00000000..9ee1219e --- /dev/null +++ b/kdb-bot/src/modules/stats/test/user_xp_asc.py @@ -0,0 +1,37 @@ +from cpl_discord.container import Guild +from cpl_query.extension import List + +from bot_data.model.auto_role import AutoRole +from bot_data.model.client import Client +from bot_data.model.known_user import KnownUser +from bot_data.model.level import Level +from bot_data.model.server import Server +from bot_data.model.user import User +from bot_data.model.user_joined_server import UserJoinedServer +from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel +from modules.stats.model.statistic_result import StatisticResult + + +async def user_xp_asc( + auto_roles: List[AutoRole], + clients: List[Client], + known_users: List[KnownUser], + levels: List[Level], + servers: List[Server], + user_joined_servers: List[UserJoinedServer], + user_joined_voice_channel: List[UserJoinedVoiceChannel], + users: List[User], + guild: Guild +) -> StatisticResult: + result = StatisticResult() + result.header.append('Name') + result.header.append('XP') + + for user in users.order_by(lambda u: u.xp): + row = List(str) + member = guild.get_member(user.discord_id) + row.append(member.name) + row.append(str(user.xp)) + result.values.append(row) + + return result diff --git a/kdb-bot/src/modules/stats/test/user_xp_desc.py b/kdb-bot/src/modules/stats/test/user_xp_desc.py new file mode 100644 index 00000000..32cd0f3e --- /dev/null +++ b/kdb-bot/src/modules/stats/test/user_xp_desc.py @@ -0,0 +1,37 @@ +from cpl_discord.container import Guild +from cpl_query.extension import List + +from bot_data.model.auto_role import AutoRole +from bot_data.model.client import Client +from bot_data.model.known_user import KnownUser +from bot_data.model.level import Level +from bot_data.model.server import Server +from bot_data.model.user import User +from bot_data.model.user_joined_server import UserJoinedServer +from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel +from modules.stats.model.statistic_result import StatisticResult + + +async def user_xp_desc( + auto_roles: List[AutoRole], + clients: List[Client], + known_users: List[KnownUser], + levels: List[Level], + servers: List[Server], + user_joined_servers: List[UserJoinedServer], + user_joined_voice_channel: List[UserJoinedVoiceChannel], + users: List[User], + guild: Guild +) -> StatisticResult: + result = StatisticResult() + result.header.append('Name') + result.header.append('XP') + + for user in users.order_by_descending(lambda u: u.xp): + row = List(str) + member = guild.get_member(user.discord_id) + row.append(member.name) + row.append(str(user.xp)) + result.values.append(row) + + return result From a57c8da72a6a88d3b278162d9056978acbf078fb Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 20:28:44 +0100 Subject: [PATCH 151/275] Added logic to call statistics #46 --- kdb-bot/cpl-workspace.json | 3 ++- kdb-bot/src/bot/module_list.py | 2 ++ kdb-bot/src/bot_api/abc/__init__.py | 2 +- kdb-bot/src/bot_core/abc/__init__.py | 2 +- .../configuration/feature_flags_enum.py | 2 +- .../configuration/feature_flags_settings.py | 1 + kdb-bot/src/bot_data/abc/__init__.py | 2 +- kdb-bot/src/modules/base/abc/__init__.py | 2 +- .../src/modules/permission/abc/__init__.py | 2 +- kdb-bot/src/modules/stats/stats_module.py | 24 +++++++++++++++++++ kdb-bot/src/modules/stats/test/__init__.py | 0 11 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 kdb-bot/src/modules/stats/stats_module.py create mode 100644 kdb-bot/src/modules/stats/test/__init__.py diff --git a/kdb-bot/cpl-workspace.json b/kdb-bot/cpl-workspace.json index fa9a4332..6587cbf6 100644 --- a/kdb-bot/cpl-workspace.json +++ b/kdb-bot/cpl-workspace.json @@ -3,6 +3,7 @@ "DefaultProject": "bot", "Projects": { "bot": "src/bot/bot.json", + "bot-api": "src/bot_api/bot-api.json", "bot-core": "src/bot_core/bot-core.json", "bot-data": "src/bot_data/bot-data.json", "auto-role": "src/modules/auto_role/auto-role.json", @@ -11,7 +12,7 @@ "database": "src/modules/database/database.json", "level": "src/modules/level/level.json", "permission": "src/modules/permission/permission.json", - "bot-api": "src/bot_api/bot-api.json", + "stats": "src/modules/stats/stats.json", "get-version": "tools/get_version/get-version.json", "post-build": "tools/post_build/post-build.json", "set-version": "tools/set_version/set-version.json" diff --git a/kdb-bot/src/bot/module_list.py b/kdb-bot/src/bot/module_list.py index 575824f3..3eb60cd3 100644 --- a/kdb-bot/src/bot/module_list.py +++ b/kdb-bot/src/bot/module_list.py @@ -10,6 +10,7 @@ from modules.boot_log.boot_log_module import BootLogModule from modules.database.database_module import DatabaseModule from modules.level.level_module import LevelModule from modules.permission.permission_module import PermissionModule +from modules.stats.stats_module import StatsModule class ModuleList: @@ -26,6 +27,7 @@ class ModuleList: LevelModule, PermissionModule, ApiModule, + StatsModule, # has to be last! BootLogModule, CoreExtensionModule, diff --git a/kdb-bot/src/bot_api/abc/__init__.py b/kdb-bot/src/bot_api/abc/__init__.py index 20368eb6..ad4cf626 100644 --- a/kdb-bot/src/bot_api/abc/__init__.py +++ b/kdb-bot/src/bot_api/abc/__init__.py @@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server """ -__title__ = 'bot_api.abc' +__title__ = 'bot_api.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' diff --git a/kdb-bot/src/bot_core/abc/__init__.py b/kdb-bot/src/bot_core/abc/__init__.py index 4056210a..1266f4f1 100644 --- a/kdb-bot/src/bot_core/abc/__init__.py +++ b/kdb-bot/src/bot_core/abc/__init__.py @@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server """ -__title__ = 'bot_core.abc' +__title__ = 'bot_core.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' diff --git a/kdb-bot/src/bot_core/configuration/feature_flags_enum.py b/kdb-bot/src/bot_core/configuration/feature_flags_enum.py index 226ca1d8..f04e5ca7 100644 --- a/kdb-bot/src/bot_core/configuration/feature_flags_enum.py +++ b/kdb-bot/src/bot_core/configuration/feature_flags_enum.py @@ -2,7 +2,6 @@ from enum import Enum class FeatureFlagsEnum(Enum): - # modules api_module = 'ApiModule' admin_module = 'AdminModule' @@ -16,6 +15,7 @@ class FeatureFlagsEnum(Enum): level_module = 'LevelModule' moderator_module = 'ModeratorModule' permission_module = 'PermissionModule' + stats_module = 'StatsModule' # features api_only = 'ApiOnly' presence = 'Presence' diff --git a/kdb-bot/src/bot_core/configuration/feature_flags_settings.py b/kdb-bot/src/bot_core/configuration/feature_flags_settings.py index 1861f9a1..dff28c6e 100644 --- a/kdb-bot/src/bot_core/configuration/feature_flags_settings.py +++ b/kdb-bot/src/bot_core/configuration/feature_flags_settings.py @@ -25,6 +25,7 @@ class FeatureFlagsSettings(ConfigurationModelABC): FeatureFlagsEnum.database_module.value: True, # 02.10.2022 #48 FeatureFlagsEnum.moderator_module.value: False, # 02.10.2022 #48 FeatureFlagsEnum.permission_module.value: True, # 02.10.2022 #48 + FeatureFlagsEnum.stats_module.value: True, # 08.11.2022 #46 # features FeatureFlagsEnum.api_only.value: False, # 13.10.2022 #70 FeatureFlagsEnum.presence.value: True, # 03.10.2022 #56 diff --git a/kdb-bot/src/bot_data/abc/__init__.py b/kdb-bot/src/bot_data/abc/__init__.py index 7209be45..c2b4323a 100644 --- a/kdb-bot/src/bot_data/abc/__init__.py +++ b/kdb-bot/src/bot_data/abc/__init__.py @@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server """ -__title__ = 'bot_data.abc' +__title__ = 'bot_data.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' diff --git a/kdb-bot/src/modules/base/abc/__init__.py b/kdb-bot/src/modules/base/abc/__init__.py index ccc3e98a..41181517 100644 --- a/kdb-bot/src/modules/base/abc/__init__.py +++ b/kdb-bot/src/modules/base/abc/__init__.py @@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server """ -__title__ = 'modules.base.abc' +__title__ = 'modules.base.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' diff --git a/kdb-bot/src/modules/permission/abc/__init__.py b/kdb-bot/src/modules/permission/abc/__init__.py index 294d277c..930246c1 100644 --- a/kdb-bot/src/modules/permission/abc/__init__.py +++ b/kdb-bot/src/modules/permission/abc/__init__.py @@ -11,7 +11,7 @@ Discord bot for the Keksdose discord Server """ -__title__ = 'modules.permission.abc' +__title__ = 'modules.permission.service' __author__ = 'Sven Heidemann' __license__ = 'MIT' __copyright__ = 'Copyright (c) 2022 sh-edraft.de' diff --git a/kdb-bot/src/modules/stats/stats_module.py b/kdb-bot/src/modules/stats/stats_module.py new file mode 100644 index 00000000..4c60b02a --- /dev/null +++ b/kdb-bot/src/modules/stats/stats_module.py @@ -0,0 +1,24 @@ +from cpl_core.configuration import ConfigurationABC +from cpl_core.dependency_injection import ServiceCollectionABC +from cpl_core.environment import ApplicationEnvironmentABC +from cpl_discord.service.discord_collection_abc import DiscordCollectionABC + +from bot_core.abc.module_abc import ModuleABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from modules.stats.command.stats_group import StatsGroup +from modules.stats.service.statistic_service import StatisticService + + +class StatsModule(ModuleABC): + + def __init__(self, dc: DiscordCollectionABC): + ModuleABC.__init__(self, dc, FeatureFlagsEnum.stats_module) + + def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): + pass + + def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): + services.add_transient(StatisticService) + # commands + self._dc.add_command(StatsGroup) + # events diff --git a/kdb-bot/src/modules/stats/test/__init__.py b/kdb-bot/src/modules/stats/test/__init__.py new file mode 100644 index 00000000..e69de29b From ddb9d0e647d0a055731d042223f31d78b7929ed5 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 20:34:47 +0100 Subject: [PATCH 152/275] Removed old code #46 --- kdb-bot/src/modules/stats/command/stats_group.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index 32b32f61..01884c7a 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -95,17 +95,6 @@ class StatsGroup(DiscordCommandABC): statistic = self._stats.where(lambda s: s.name == name).single() result = await self._statistic.execute(statistic.func, server) - # headers = '' - # rows = '' - # for header in result.header: - # headers += f'\n{header}' - # - # for row in result.values: - # row_str = '' - # for column in row: - # row_str += f'\n{column}' - # rows += f'\n{row_str}' - embed = discord.Embed( title=statistic.name, description=statistic.description, @@ -119,9 +108,7 @@ class StatsGroup(DiscordCommandABC): value += f'\n{row[i]}' embed.add_field(name=header, value=value, inline=True) - # embed.add_field(name=self._t.transform('modules.auto_role.list.message_id'), value=rows, inline=True) await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait) - # await self._message_service.send_ctx_msg(ctx, name, wait_before_delete=wait) except Exception as e: self._logger.error(__name__, f'Cannot view statistic {name}', e) await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.view.failed')) From 61b2432d0bd6027722eeb8eeed2a182295ff8a21 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 21:54:33 +0100 Subject: [PATCH 153/275] Added logic to handle custom statistics #46 --- kdb-bot/src/bot/translation/de.json | 4 ++ .../src/bot_core/abc/message_service_abc.py | 14 +++-- .../src/bot_core/service/message_service.py | 30 +++++++++++ .../src/modules/stats/command/stats_group.py | 52 +++++++++++++++---- kdb-bot/src/modules/stats/model/statistic.py | 8 +-- .../stats/service/statistic_service.py | 33 ++++++++++-- kdb-bot/src/modules/stats/ui/__init__.py | 1 + .../modules/stats/ui/add_statistic_form.py | 35 +++++++++++++ 8 files changed, 154 insertions(+), 23 deletions(-) create mode 100644 kdb-bot/src/modules/stats/ui/__init__.py create mode 100644 kdb-bot/src/modules/stats/ui/add_statistic_form.py diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index edb2e87c..49f675b6 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -227,6 +227,10 @@ "statistic": "Statistik", "description": "Beschreibung", "failed": "Statistik kann nicht gezeigt werden :(" + }, + "add": { + "failed": "Statistik kann nicht hinzugefügt werden :(", + "success": "Statistik wurde hinzugefügt :D" } } }, diff --git a/kdb-bot/src/bot_core/abc/message_service_abc.py b/kdb-bot/src/bot_core/abc/message_service_abc.py index 3e524d4d..614dd1df 100644 --- a/kdb-bot/src/bot_core/abc/message_service_abc.py +++ b/kdb-bot/src/bot_core/abc/message_service_abc.py @@ -3,6 +3,7 @@ from typing import Union import discord from cpl_query.extension import List +from discord import Interaction from discord.ext.commands import Context @@ -10,18 +11,21 @@ class MessageServiceABC(ABC): @abstractmethod def __init__(self): pass - + @abstractmethod async def delete_messages(self, messages: List[discord.Message], guild_id: int, without_tracking=False): pass - + @abstractmethod async def delete_message(self, message: discord.Message, without_tracking=False): pass - + @abstractmethod async def send_channel_message(self, channel: discord.TextChannel, message: Union[str, discord.Embed], without_tracking=True): pass - + @abstractmethod async def send_dm_message(self, message: Union[str, discord.Embed], receiver: Union[discord.User, discord.Member], without_tracking=False): pass - + @abstractmethod async def send_ctx_msg(self, ctx: Context, message: Union[str, discord.Embed], file: discord.File = None, is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass + + @abstractmethod + async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass diff --git a/kdb-bot/src/bot_core/service/message_service.py b/kdb-bot/src/bot_core/service/message_service.py index 6a43f32c..5d211c59 100644 --- a/kdb-bot/src/bot_core/service/message_service.py +++ b/kdb-bot/src/bot_core/service/message_service.py @@ -6,6 +6,7 @@ from cpl_core.configuration.configuration_abc import ConfigurationABC from cpl_core.database.context.database_context_abc import DatabaseContextABC from cpl_discord.service import DiscordBotServiceABC from cpl_query.extension import List +from discord import Interaction from discord.ext.commands import Context from bot_core.abc.message_service_abc import MessageServiceABC @@ -117,3 +118,32 @@ class MessageService(MessageServiceABC): if ctx.guild is not None: await self.delete_message(msg, without_tracking) + + async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=False): + if interaction is None: + self._logger.warn(__name__, 'Message context is empty') + self._logger.debug(__name__, f'Message: {message}') + return + + self._logger.debug(__name__, f'Try to send message\t\t{message}\n\tto: {interaction.channel}') + msg = None + try: + if isinstance(message, discord.Embed): + msg = await interaction.response.send_message(embed=message) + else: + msg = await interaction.response.send_message(message) + except Exception as e: + self._logger.error(__name__, f'Send message to channel {interaction.channel.id} failed', e) + else: + self._logger.info(__name__, f'Sent message to channel {interaction.channel.id}') + if not without_tracking and interaction.guild is not None: + self._clients.append_sent_message_count(self._bot.user.id, interaction.guild.id, 1) + self._db.save_changes() + + if wait_before_delete is not None: + await asyncio.sleep(wait_before_delete) + + if is_persistent: + return + + await self.delete_message(msg, without_tracking) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index 01884c7a..bb46b0a0 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -1,3 +1,4 @@ +import textwrap from typing import List as TList import discord @@ -15,8 +16,25 @@ from bot_data.abc.server_repository_abc import ServerRepositoryABC from modules.permission.abc.permission_service_abc import PermissionServiceABC from modules.stats.model.statistic import Statistic from modules.stats.service.statistic_service import StatisticService -from modules.stats.test.user_xp_asc import user_xp_asc -from modules.stats.test.user_xp_desc import user_xp_desc +from modules.stats.ui.add_statistic_form import AddStatisticForm + +stats = List(Statistic, [ + Statistic( + 'Benutzer XP Aufsteigend', + 'Zeigt XP von jedem Benutzer, aufsteigend nach XP', + textwrap.dedent("""\ + result.header.append('Name') + result.header.append('XP') + + for user in users.order_by(lambda u: u.xp): + row = List(str) + member = guild.get_member(user.discord_id) + row.append(member.name) + row.append(str(user.xp)) + result.values.append(row) + """)), + Statistic('Benutzer XP Absteigend', 'Zeigt XP von jedem Benutzer, absteigend nach XP', '') +]) class StatsGroup(DiscordCommandABC): @@ -41,17 +59,12 @@ class StatsGroup(DiscordCommandABC): self._statistic = statistic self._servers = servers - self._stats = List(Statistic, [ - Statistic('Benutzer XP Aufsteigend', 'Zeigt XP von jedem Benutzer, aufsteigend nach XP', user_xp_asc), - Statistic('Benutzer XP Absteigend', 'Zeigt XP von jedem Benutzer, absteigend nach XP', user_xp_desc) - ]) - @commands.hybrid_group() @commands.guild_only() async def stats(self, ctx: Context): pass - @stats.command(alias='rules') + @stats.command() @commands.guild_only() async def list(self, ctx: Context, wait: int = None): self._logger.debug(__name__, f'Received command stats list {ctx}') @@ -92,8 +105,8 @@ class StatsGroup(DiscordCommandABC): try: server = self._servers.get_server_by_discord_id(ctx.guild.id) - statistic = self._stats.where(lambda s: s.name == name).single() - result = await self._statistic.execute(statistic.func, server) + statistic = stats.where(lambda s: s.name == name).single() + result = await self._statistic.execute(statistic.code, server) embed = discord.Embed( title=statistic.name, @@ -117,4 +130,21 @@ class StatsGroup(DiscordCommandABC): @view.autocomplete('name') async def view_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: - return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in self._stats] + return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] + + @stats.command() + @commands.guild_only() + async def add(self, ctx: Context, name: str): + self._logger.debug(__name__, f'Received command stats list {ctx}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_technician(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command stats list') + return + + form = AddStatisticForm(stats, name, self._message_service, self._logger, self._t) + self._logger.trace(__name__, f'Finished stats command') + self._logger.trace(__name__, f'Started stats command form') + await ctx.interaction.response.send_modal(form) diff --git a/kdb-bot/src/modules/stats/model/statistic.py b/kdb-bot/src/modules/stats/model/statistic.py index 2db1b75d..d1b6558d 100644 --- a/kdb-bot/src/modules/stats/model/statistic.py +++ b/kdb-bot/src/modules/stats/model/statistic.py @@ -3,10 +3,10 @@ from typing import Callable class Statistic: - def __init__(self, name: str, description: str, func: Callable): + def __init__(self, name: str, description: str, code: str): self._name = name self._description = description - self._func = func + self._code = code @property def name(self) -> str: @@ -17,5 +17,5 @@ class Statistic: return self._description @property - def func(self) -> Callable: - return self._func + def code(self) -> str: + return self._code diff --git a/kdb-bot/src/modules/stats/service/statistic_service.py b/kdb-bot/src/modules/stats/service/statistic_service.py index b0f59d62..cee56335 100644 --- a/kdb-bot/src/modules/stats/service/statistic_service.py +++ b/kdb-bot/src/modules/stats/service/statistic_service.py @@ -1,7 +1,8 @@ from abc import abstractmethod -from typing import Callable from cpl_discord.service import DiscordBotServiceABC +from cpl_query.extension import List +from discord import Guild from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.client_repository_abc import ClientRepositoryABC @@ -11,7 +12,14 @@ from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC +from bot_data.model.auto_role import AutoRole +from bot_data.model.client import Client +from bot_data.model.known_user import KnownUser +from bot_data.model.level import Level from bot_data.model.server import Server +from bot_data.model.user import User +from bot_data.model.user_joined_server import UserJoinedServer +from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel from modules.stats.model.statistic_result import StatisticResult @@ -39,10 +47,11 @@ class StatisticService: self._users = users self._bot = bot - async def execute(self, _f: Callable, server: Server) -> StatisticResult: + async def execute(self, code: str, server: Server) -> StatisticResult: guild = self._bot.guilds.where(lambda g: g.id == server.discord_server_id).single() - return await _f( + return await self.get_data( + code, self._auto_roles .get_auto_roles() .where(lambda x: x.server.server_id == server.server_id), @@ -67,3 +76,21 @@ class StatisticService: .where(lambda x: x.server.server_id == server.server_id), guild ) + + @staticmethod + async def get_data( + code: str, + auto_roles: List[AutoRole], + clients: List[Client], + known_users: List[KnownUser], + levels: List[Level], + servers: List[Server], + user_joined_servers: List[UserJoinedServer], + user_joined_voice_channel: List[UserJoinedVoiceChannel], + users: List[User], + guild: Guild + ) -> StatisticResult: + result = StatisticResult() + exec(code) + + return result diff --git a/kdb-bot/src/modules/stats/ui/__init__.py b/kdb-bot/src/modules/stats/ui/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/kdb-bot/src/modules/stats/ui/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/kdb-bot/src/modules/stats/ui/add_statistic_form.py b/kdb-bot/src/modules/stats/ui/add_statistic_form.py new file mode 100644 index 00000000..9591fb21 --- /dev/null +++ b/kdb-bot/src/modules/stats/ui/add_statistic_form.py @@ -0,0 +1,35 @@ +import discord +from cpl_query.extension import List +from cpl_translation import TranslatePipe +from discord import ui, TextStyle + +from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.logging.command_logger import CommandLogger +from modules.stats.model.statistic import Statistic + + +class AddStatisticForm(ui.Modal): + + description = ui.TextInput(label='Beschreibung', required=True) + code = ui.TextInput(label='Code', required=True, style=TextStyle.long) + + def __init__( + self, + stats: List[Statistic], + name: str, + message_service: MessageServiceABC, + logger: CommandLogger, + t: TranslatePipe, + ): + ui.Modal.__init__(self, title=name) + + self._stats = stats + self._name = name + self._message_service = message_service + self._logger = logger + self._t = t + + async def on_submit(self, interaction: discord.Interaction): + self._stats.append(Statistic(self._name, self.description.value, self.code.value)) + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.success')) + self._logger.trace(__name__, f'Finished stats command form') From 86433e841b6af1d73c72f71230cb5087a5945217 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 8 Nov 2022 22:06:36 +0100 Subject: [PATCH 154/275] Fixed typo #46 --- kdb-bot/src/modules/stats/command/stats_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index bb46b0a0..b1051209 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -84,7 +84,7 @@ class StatsGroup(DiscordCommandABC): statistics = '' descriptions = '' - for statistic in self._stats: + for statistic in stats: statistics += f'\n{statistic["Name"]}' descriptions += f'\n{statistic["Description"]}' From 5c7db72723fab2d7bacdc10da812dfd8851291f0 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 17:18:14 +0100 Subject: [PATCH 155/275] Fixed level seeder #46 --- kdb-bot/src/modules/level/level_seeder.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py index 8e6eb9e7..ed3d25ba 100644 --- a/kdb-bot/src/modules/level/level_seeder.py +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -1,6 +1,7 @@ import discord -from cpl_discord.container import Guild +from cpl_discord.container import Guild, Role from cpl_discord.service import DiscordBotServiceABC +from cpl_query.extension import List from discord import Permissions, Colour from bot_core.logging.database_logger import DatabaseLogger From 8cd67cd68cce4c1a168aa3d3933860a7236ae5c4 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 17:59:55 +0100 Subject: [PATCH 156/275] Added stats edit command #46 --- kdb-bot/src/bot/translation/de.json | 4 +++ .../src/bot_core/service/message_service.py | 7 ++-- .../src/modules/stats/command/stats_group.py | 32 +++++++++++++++++-- kdb-bot/src/modules/stats/model/statistic.py | 8 +++++ .../modules/stats/ui/add_statistic_form.py | 27 ++++++++++++++-- 5 files changed, 69 insertions(+), 9 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 49f675b6..6dd03c73 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -231,6 +231,10 @@ "add": { "failed": "Statistik kann nicht hinzugefügt werden :(", "success": "Statistik wurde hinzugefügt :D" + }, + "edit": { + "failed": "Statistik kann nicht bearbeitet werden :(", + "success": "Statistik wurde gespeichert :D" } } }, diff --git a/kdb-bot/src/bot_core/service/message_service.py b/kdb-bot/src/bot_core/service/message_service.py index 5d211c59..04f33bfb 100644 --- a/kdb-bot/src/bot_core/service/message_service.py +++ b/kdb-bot/src/bot_core/service/message_service.py @@ -126,12 +126,11 @@ class MessageService(MessageServiceABC): return self._logger.debug(__name__, f'Try to send message\t\t{message}\n\tto: {interaction.channel}') - msg = None try: if isinstance(message, discord.Embed): - msg = await interaction.response.send_message(embed=message) + await interaction.response.send_message(embed=message) else: - msg = await interaction.response.send_message(message) + await interaction.response.send_message(message) except Exception as e: self._logger.error(__name__, f'Send message to channel {interaction.channel.id} failed', e) else: @@ -146,4 +145,4 @@ class MessageService(MessageServiceABC): if is_persistent: return - await self.delete_message(msg, without_tracking) + await self.delete_message(await interaction.original_response(), without_tracking) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index b1051209..58105c0b 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -135,16 +135,42 @@ class StatsGroup(DiscordCommandABC): @stats.command() @commands.guild_only() async def add(self, ctx: Context, name: str): - self._logger.debug(__name__, f'Received command stats list {ctx}') + self._logger.debug(__name__, f'Received command stats add {ctx}: {name}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return if not self._permissions.is_member_technician(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command stats list') + self._logger.trace(__name__, f'Finished command stats add') return form = AddStatisticForm(stats, name, self._message_service, self._logger, self._t) - self._logger.trace(__name__, f'Finished stats command') + self._logger.trace(__name__, f'Finished stats add command') self._logger.trace(__name__, f'Started stats command form') await ctx.interaction.response.send_modal(form) + + @stats.command() + @commands.guild_only() + async def edit(self, ctx: Context, name: str): + self._logger.debug(__name__, f'Received command stats edit {ctx}: {name}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_technician(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command stats edit') + return + + try: + statistic = stats.where(lambda s: s.name == name).single() + form = AddStatisticForm(stats, name, self._message_service, self._logger, self._t, code=statistic.code, description=statistic.description) + self._logger.trace(__name__, f'Finished stats edit command') + self._logger.trace(__name__, f'Started stats command form') + await ctx.interaction.response.send_modal(form) + except Exception as e: + self._logger.error(__name__, f'Cannot view statistic {name}', e) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.edit.failed')) + + @edit.autocomplete('name') + async def edit_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] diff --git a/kdb-bot/src/modules/stats/model/statistic.py b/kdb-bot/src/modules/stats/model/statistic.py index d1b6558d..1ab0afe4 100644 --- a/kdb-bot/src/modules/stats/model/statistic.py +++ b/kdb-bot/src/modules/stats/model/statistic.py @@ -16,6 +16,14 @@ class Statistic: def description(self) -> str: return self._description + @description.setter + def description(self, value: str): + self._description = value + @property def code(self) -> str: return self._code + + @code.setter + def code(self, value: str): + self._code = value diff --git a/kdb-bot/src/modules/stats/ui/add_statistic_form.py b/kdb-bot/src/modules/stats/ui/add_statistic_form.py index 9591fb21..fe053241 100644 --- a/kdb-bot/src/modules/stats/ui/add_statistic_form.py +++ b/kdb-bot/src/modules/stats/ui/add_statistic_form.py @@ -20,6 +20,8 @@ class AddStatisticForm(ui.Modal): message_service: MessageServiceABC, logger: CommandLogger, t: TranslatePipe, + code: str = None, + description: str = None, ): ui.Modal.__init__(self, title=name) @@ -29,7 +31,28 @@ class AddStatisticForm(ui.Modal): self._logger = logger self._t = t + if code is not None: + self.code.default = code + + if description is not None: + self.description.default = description + async def on_submit(self, interaction: discord.Interaction): - self._stats.append(Statistic(self._name, self.description.value, self.code.value)) - await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.success')) + statistic = self._stats.where(lambda s: s.name == self._name).single_or_default() + try: + if statistic is None: + self._stats.append(Statistic(self._name, self.description.value, self.code.value)) + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.success')) + return + + statistic.description = self.description.value + statistic.code = self.code.value + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.edit.success')) + except Exception as e: + self._logger.error(__name__, f'Save statistic {self._name} failed', e) + if statistic is None: + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.failed')) + else: + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.edit.failed')) + self._logger.trace(__name__, f'Finished stats command form') From bf107f9a1894d129efc7140389757b7ca3fa6d71 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 18:01:14 +0100 Subject: [PATCH 157/275] Added stats remove command #46 --- .../src/modules/stats/command/stats_group.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index 58105c0b..67c8469a 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -168,9 +168,32 @@ class StatsGroup(DiscordCommandABC): self._logger.trace(__name__, f'Started stats command form') await ctx.interaction.response.send_modal(form) except Exception as e: - self._logger.error(__name__, f'Cannot view statistic {name}', e) + self._logger.error(__name__, f'Cannot edit statistic {name}', e) await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.edit.failed')) @edit.autocomplete('name') async def edit_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] + + @stats.command() + @commands.guild_only() + async def remove(self, ctx: Context, name: str): + self._logger.debug(__name__, f'Received command stats remove {ctx}: {name}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + + if not self._permissions.is_member_technician(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command stats remove') + return + + try: + stats.remove(stats.where(lambda s: s.name == name).single()) + self._logger.trace(__name__, f'Finished stats remove command') + except Exception as e: + self._logger.error(__name__, f'Cannot remove statistic {name}', e) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.edit.failed')) + + @remove.autocomplete('name') + async def edit_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] From b5080d8c04c4f41acb2351e2f8db863a74726763 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 18:22:27 +0100 Subject: [PATCH 158/275] Added stats migration #46 --- .../src/bot/startup_migration_extension.py | 2 ++ .../src/bot_data/migration/stats_migration.py | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 kdb-bot/src/bot_data/migration/stats_migration.py diff --git a/kdb-bot/src/bot/startup_migration_extension.py b/kdb-bot/src/bot/startup_migration_extension.py index a5d75b6d..724db39a 100644 --- a/kdb-bot/src/bot/startup_migration_extension.py +++ b/kdb-bot/src/bot/startup_migration_extension.py @@ -8,6 +8,7 @@ from bot_data.migration.api_migration import ApiMigration from bot_data.migration.auto_role_migration import AutoRoleMigration from bot_data.migration.initial_migration import InitialMigration from bot_data.migration.level_migration import LevelMigration +from bot_data.migration.stats_migration import StatsMigration from bot_data.service.migration_service import MigrationService @@ -25,3 +26,4 @@ class StartupMigrationExtension(StartupExtensionABC): services.add_transient(MigrationABC, AutoRoleMigration) # 03.10.2022 #54 - 0.2.2 services.add_transient(MigrationABC, ApiMigration) # 15.10.2022 #70 - 0.3.0 services.add_transient(MigrationABC, LevelMigration) # 06.11.2022 #25 - 0.3.0 + services.add_transient(MigrationABC, StatsMigration) # 09.11.2022 #46 - 0.3.0 diff --git a/kdb-bot/src/bot_data/migration/stats_migration.py b/kdb-bot/src/bot_data/migration/stats_migration.py new file mode 100644 index 00000000..92ba17ab --- /dev/null +++ b/kdb-bot/src/bot_data/migration/stats_migration.py @@ -0,0 +1,36 @@ +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.migration_abc import MigrationABC +from bot_data.db_context import DBContext + + +class StatsMigration(MigrationABC): + name = '0.3_StatsMigration' + + def __init__(self, logger: DatabaseLogger, db: DBContext): + MigrationABC.__init__(self) + self._logger = logger + self._db = db + self._cursor = db.cursor + + def upgrade(self): + self._logger.debug(__name__, 'Running upgrade') + + self._cursor.execute( + str(f""" + CREATE TABLE IF NOT EXISTS `Statistics` ( + `Id` BIGINT NOT NULL AUTO_INCREMENT, + `Name` VARCHAR(255) NOT NULL, + `Description` VARCHAR(255) NOT NULL, + `Code` LONGTEXT NOT NULL, + `ServerId` BIGINT, + `CreatedAt` DATETIME(6), + `LastModifiedAt` DATETIME(6), + PRIMARY KEY(`Id`), + FOREIGN KEY (`ServerId`) REFERENCES `Servers`(`ServerId`) + ); + """) + ) + + def downgrade(self): + self._cursor.execute('DROP TABLE `Statistics`;') + From 1c9c265ba82a279a367b2df4039e2bfae565cb26 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 18:36:48 +0100 Subject: [PATCH 159/275] Added stats table #46 --- kdb-bot/src/bot_data/model/statistic.py | 108 ++++++++++++++++++ .../src/modules/stats/command/stats_group.py | 2 +- kdb-bot/src/modules/stats/model/statistic.py | 29 ----- .../modules/stats/ui/add_statistic_form.py | 6 +- 4 files changed, 112 insertions(+), 33 deletions(-) create mode 100644 kdb-bot/src/bot_data/model/statistic.py delete mode 100644 kdb-bot/src/modules/stats/model/statistic.py diff --git a/kdb-bot/src/bot_data/model/statistic.py b/kdb-bot/src/bot_data/model/statistic.py new file mode 100644 index 00000000..1c34a264 --- /dev/null +++ b/kdb-bot/src/bot_data/model/statistic.py @@ -0,0 +1,108 @@ +from datetime import datetime +from typing import Optional + +from cpl_core.database import TableABC + +from bot_data.model.server import Server + + +class Statistic(TableABC): + + def __init__(self, name: str, description: str, code: str, server: Server, created_at: datetime=None, modified_at: datetime=None, id=0): + self._id = id + self._name = name + self._description = description + self._code = code + self._server = server + + TableABC.__init__(self) + self._created_at = created_at if created_at is not None else self._created_at + self._modified_at = modified_at if modified_at is not None else self._modified_at + + @property + def id(self) -> int: + return self._id + + @property + def name(self) -> str: + return self._name + + @property + def description(self) -> str: + return self._description + + @description.setter + def description(self, value: str): + self._description = value + + @property + def code(self) -> str: + return self._code + + @code.setter + def code(self, value: str): + self._code = value + + @property + def server(self) -> Server: + return self._server + + @staticmethod + def get_select_all_string() -> str: + return str(f""" + SELECT * FROM `Statistics`; + """) + + @staticmethod + def get_select_by_id_string(id: int) -> str: + return str(f""" + SELECT * FROM `Statistics` + WHERE `Id` = {id}; + """) + + @staticmethod + def get_select_by_name_string(name: str, s_id: int) -> str: + return str(f""" + SELECT * FROM `Statistics` + WHERE `Name` = {name}; + """) + + @staticmethod + def get_select_by_server_string(s_id: int) -> str: + return str(f""" + SELECT * FROM `Statistics` + WHERE `ServerId` = {s_id}; + """) + + @property + def insert_string(self) -> str: + return str(f""" + INSERT INTO `Statistics` ( + `Name`, `Description`, `Code`, `ServerId`, `CreatedAt`, `LastModifiedAt` + ) VALUES ( + '{self._name}', + '{self._description}', + '{self._code}', + {self._server.server_id}, + '{self._created_at}', + '{self._modified_at}' + ); + """) + + @property + def udpate_string(self) -> str: + return str(f""" + UPDATE `Statistics` + SET `Name` = {self._name}, + `Description` = '{self._description}' + `Code` = '{self._code}' + `LastModifiedAt` = '{self._modified_at}' + WHERE `Id` = {self._id}; + """) + + @property + def delete_string(self) -> str: + return str(f""" + DELETE FROM `Statistics` + WHERE `Id` = {self._id}; + """) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index 67c8469a..0d61f249 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -13,8 +13,8 @@ from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.logging.command_logger import CommandLogger from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.model.statistic import Statistic from modules.permission.abc.permission_service_abc import PermissionServiceABC -from modules.stats.model.statistic import Statistic from modules.stats.service.statistic_service import StatisticService from modules.stats.ui.add_statistic_form import AddStatisticForm diff --git a/kdb-bot/src/modules/stats/model/statistic.py b/kdb-bot/src/modules/stats/model/statistic.py deleted file mode 100644 index 1ab0afe4..00000000 --- a/kdb-bot/src/modules/stats/model/statistic.py +++ /dev/null @@ -1,29 +0,0 @@ -from typing import Callable - - -class Statistic: - - def __init__(self, name: str, description: str, code: str): - self._name = name - self._description = description - self._code = code - - @property - def name(self) -> str: - return self._name - - @property - def description(self) -> str: - return self._description - - @description.setter - def description(self, value: str): - self._description = value - - @property - def code(self) -> str: - return self._code - - @code.setter - def code(self, value: str): - self._code = value diff --git a/kdb-bot/src/modules/stats/ui/add_statistic_form.py b/kdb-bot/src/modules/stats/ui/add_statistic_form.py index fe053241..073fc9d4 100644 --- a/kdb-bot/src/modules/stats/ui/add_statistic_form.py +++ b/kdb-bot/src/modules/stats/ui/add_statistic_form.py @@ -5,7 +5,7 @@ from discord import ui, TextStyle from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.logging.command_logger import CommandLogger -from modules.stats.model.statistic import Statistic +from modules.stats.model.statisticmodel import StatisticModel class AddStatisticForm(ui.Modal): @@ -15,7 +15,7 @@ class AddStatisticForm(ui.Modal): def __init__( self, - stats: List[Statistic], + stats: List[StatisticModel], name: str, message_service: MessageServiceABC, logger: CommandLogger, @@ -41,7 +41,7 @@ class AddStatisticForm(ui.Modal): statistic = self._stats.where(lambda s: s.name == self._name).single_or_default() try: if statistic is None: - self._stats.append(Statistic(self._name, self.description.value, self.code.value)) + self._stats.append(StatisticModel(self._name, self.description.value, self.code.value)) await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.success')) return From b52cdf0f3447cb66c1bbc406a4fccf06fc6c418b Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 18:49:11 +0100 Subject: [PATCH 160/275] Added stats repo #46 --- .../bot_data/abc/statistic_repository_abc.py | 36 ++++++++ .../service/statistic_repository_service.py | 88 +++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 kdb-bot/src/bot_data/abc/statistic_repository_abc.py create mode 100644 kdb-bot/src/bot_data/service/statistic_repository_service.py diff --git a/kdb-bot/src/bot_data/abc/statistic_repository_abc.py b/kdb-bot/src/bot_data/abc/statistic_repository_abc.py new file mode 100644 index 00000000..bf16055d --- /dev/null +++ b/kdb-bot/src/bot_data/abc/statistic_repository_abc.py @@ -0,0 +1,36 @@ +from abc import ABC, abstractmethod +from typing import Optional + +from cpl_query.extension import List + +from bot_data.model.statistic import Statistic + + +class StatisticRepositoryABC(ABC): + + @abstractmethod + def __init__(self): pass + + @abstractmethod + def get_statistics(self) -> List[Statistic]: pass + + @abstractmethod + def get_statistics_by_server_id(self, server_id: int) -> List[Statistic]: pass + + @abstractmethod + def get_statistic_by_id(self, id: int) -> Statistic: pass + + @abstractmethod + def get_statistic_by_name(self, name: str, server_id: int) -> Statistic: pass + + @abstractmethod + def find_statistic_by_name(self, name: str, server_id: int) -> Optional[Statistic]: pass + + @abstractmethod + def add_statistic(self, statistic: Statistic): pass + + @abstractmethod + def update_statistic(self, statistic: Statistic): pass + + @abstractmethod + def delete_statistic(self, statistic: Statistic): pass diff --git a/kdb-bot/src/bot_data/service/statistic_repository_service.py b/kdb-bot/src/bot_data/service/statistic_repository_service.py new file mode 100644 index 00000000..222bdf38 --- /dev/null +++ b/kdb-bot/src/bot_data/service/statistic_repository_service.py @@ -0,0 +1,88 @@ +from typing import Optional + +from cpl_core.database.context import DatabaseContextABC +from cpl_query.extension import List + +from bot_core.logging.database_logger import DatabaseLogger +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.statistic_repository_abc import StatisticRepositoryABC +from bot_data.model.statistic import Statistic + + +class StatisticRepositoryService(StatisticRepositoryABC): + + def __init__(self, logger: DatabaseLogger, db_context: DatabaseContextABC, statistics: ServerRepositoryABC): + self._logger = logger + self._context = db_context + + self._statistics = statistics + + StatisticRepositoryABC.__init__(self) + + @staticmethod + def _get_value_from_result(value: any) -> Optional[any]: + if isinstance(value, str) and 'NULL' in value: + return None + + return value + + def _statistic_from_result(self, sql_result: tuple) -> Statistic: + statistic = Statistic( + self._get_value_from_result(sql_result[1]), + self._get_value_from_result(sql_result[2]), + self._get_value_from_result(sql_result[3]), + self._statistics.get_server_by_id(sql_result[4]), + id=self._get_value_from_result(sql_result[0]) + ) + + return statistic + + def get_statistics(self) -> List[Statistic]: + statistics = List(Statistic) + self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_all_string()}') + results = self._context.select(Statistic.get_select_all_string()) + for result in results: + statistics.append(self._statistic_from_result(result)) + + return statistics + + def get_statistics_by_server_id(self, server_id: int) -> List[Statistic]: + statistics = List(Statistic) + self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_by_server_string(server_id)}') + results = self._context.select(Statistic.get_select_by_server_string(server_id)) + for result in results: + statistics.append(self._statistic_from_result(result)) + + return statistics + + def get_statistic_by_id(self, id: int) -> Statistic: + self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_by_id_string(id)}') + result = self._context.select(Statistic.get_select_by_id_string(id))[0] + return self._statistic_from_result(result) + + def get_statistic_by_name(self, name: str, server_id: int) -> Statistic: + self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_by_name_string(name, server_id)}') + result = self._context.select(Statistic.get_select_by_name_string(name, server_id))[0] + return self._statistic_from_result(result) + + def find_statistic_by_name(self, name: str, server_id: int) -> Optional[Statistic]: + self._logger.trace(__name__, f'Send SQL command: {Statistic.get_select_by_name_string(name, server_id)}') + result = self._context.select(Statistic.get_select_by_name_string(name, server_id)) + if result is None or len(result) == 0: + return None + + result = result[0] + + return self._statistic_from_result(result) + + def add_statistic(self, statistic: Statistic): + self._logger.trace(__name__, f'Send SQL command: {statistic.insert_string}') + self._context.cursor.execute(statistic.insert_string) + + def update_statistic(self, statistic: Statistic): + self._logger.trace(__name__, f'Send SQL command: {statistic.udpate_string}') + self._context.cursor.execute(statistic.udpate_string) + + def delete_statistic(self, statistic: Statistic): + self._logger.trace(__name__, f'Send SQL command: {statistic.delete_string}') + self._context.cursor.execute(statistic.delete_string) From 7fe0f19adffaf7c55146eb9429c1ca2e5b6f1171 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 19:23:10 +0100 Subject: [PATCH 161/275] Added db to stats #46 --- kdb-bot/src/bot/translation/de.json | 7 +- kdb-bot/src/bot_data/data_module.py | 3 + kdb-bot/src/bot_data/model/statistic.py | 17 ++--- .../service/statistic_repository_service.py | 7 +- .../src/modules/stats/command/stats_group.py | 66 +++++++++++-------- .../modules/stats/ui/add_statistic_form.py | 26 ++++++-- 6 files changed, 85 insertions(+), 41 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 6dd03c73..606e89c6 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -221,7 +221,8 @@ "stats": { "list": { "statistic": "Statistik", - "description": "Beschreibung" + "description": "Beschreibung", + "nothing_found": "Keine Statistiken gefunden." }, "view": { "statistic": "Statistik", @@ -235,6 +236,10 @@ "edit": { "failed": "Statistik kann nicht bearbeitet werden :(", "success": "Statistik wurde gespeichert :D" + }, + "remove": { + "failed": "Statistik kann nicht gelöscht werden :(", + "success": "Statistik wurde gelöscht :D" } } }, diff --git a/kdb-bot/src/bot_data/data_module.py b/kdb-bot/src/bot_data/data_module.py index 0668bffe..e32d8039 100644 --- a/kdb-bot/src/bot_data/data_module.py +++ b/kdb-bot/src/bot_data/data_module.py @@ -11,6 +11,7 @@ from bot_data.abc.client_repository_abc import ClientRepositoryABC from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC from bot_data.abc.level_repository_abc import LevelRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.statistic_repository_abc import StatisticRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC @@ -21,6 +22,7 @@ from bot_data.service.known_user_repository_service import KnownUserRepositorySe from bot_data.service.level_repository_service import LevelRepositoryService from bot_data.service.seeder_service import SeederService from bot_data.service.server_repository_service import ServerRepositoryService +from bot_data.service.statistic_repository_service import StatisticRepositoryService from bot_data.service.user_joined_server_repository_service import UserJoinedServerRepositoryService from bot_data.service.user_joined_voice_channel_service import UserJoinedVoiceChannelRepositoryService from bot_data.service.user_repository_service import UserRepositoryService @@ -44,5 +46,6 @@ class DataModule(ModuleABC): services.add_transient(UserJoinedVoiceChannelRepositoryABC, UserJoinedVoiceChannelRepositoryService) services.add_transient(AutoRoleRepositoryABC, AutoRoleRepositoryService) services.add_transient(LevelRepositoryABC, LevelRepositoryService) + services.add_transient(StatisticRepositoryABC, StatisticRepositoryService) services.add_transient(SeederService) diff --git a/kdb-bot/src/bot_data/model/statistic.py b/kdb-bot/src/bot_data/model/statistic.py index 1c34a264..11011237 100644 --- a/kdb-bot/src/bot_data/model/statistic.py +++ b/kdb-bot/src/bot_data/model/statistic.py @@ -1,7 +1,7 @@ from datetime import datetime -from typing import Optional from cpl_core.database import TableABC +from cpl_core.utils import CredentialManager from bot_data.model.server import Server @@ -12,7 +12,7 @@ class Statistic(TableABC): self._id = id self._name = name self._description = description - self._code = code + self._code = CredentialManager.encrypt(code) self._server = server TableABC.__init__(self) @@ -37,11 +37,11 @@ class Statistic(TableABC): @property def code(self) -> str: - return self._code + return CredentialManager.decrypt(self._code) @code.setter def code(self, value: str): - self._code = value + self._code = CredentialManager.encrypt(value) @property def server(self) -> Server: @@ -64,7 +64,8 @@ class Statistic(TableABC): def get_select_by_name_string(name: str, s_id: int) -> str: return str(f""" SELECT * FROM `Statistics` - WHERE `Name` = {name}; + WHERE `ServerId` = {s_id} + AND `Name` = '{name}'; """) @staticmethod @@ -93,9 +94,9 @@ class Statistic(TableABC): def udpate_string(self) -> str: return str(f""" UPDATE `Statistics` - SET `Name` = {self._name}, - `Description` = '{self._description}' - `Code` = '{self._code}' + SET `Name` = '{self._name}', + `Description` = '{self._description}', + `Code` = '{self._code}', `LastModifiedAt` = '{self._modified_at}' WHERE `Id` = {self._id}; """) diff --git a/kdb-bot/src/bot_data/service/statistic_repository_service.py b/kdb-bot/src/bot_data/service/statistic_repository_service.py index 222bdf38..8f31dbe2 100644 --- a/kdb-bot/src/bot_data/service/statistic_repository_service.py +++ b/kdb-bot/src/bot_data/service/statistic_repository_service.py @@ -1,6 +1,7 @@ from typing import Optional from cpl_core.database.context import DatabaseContextABC +from cpl_core.utils import CredentialManager from cpl_query.extension import List from bot_core.logging.database_logger import DatabaseLogger @@ -27,10 +28,14 @@ class StatisticRepositoryService(StatisticRepositoryABC): return value def _statistic_from_result(self, sql_result: tuple) -> Statistic: + code = self._get_value_from_result(sql_result[3]) + if code is not None: + code = CredentialManager.decrypt(code) + statistic = Statistic( self._get_value_from_result(sql_result[1]), self._get_value_from_result(sql_result[2]), - self._get_value_from_result(sql_result[3]), + code, self._statistics.get_server_by_id(sql_result[4]), id=self._get_value_from_result(sql_result[0]) ) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index 0d61f249..96734119 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -1,9 +1,8 @@ -import textwrap from typing import List as TList import discord +from cpl_core.database.context import DatabaseContextABC from cpl_discord.command import DiscordCommandABC -from cpl_query.extension import List from cpl_translation import TranslatePipe from discord import app_commands from discord.ext import commands @@ -13,29 +12,11 @@ from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.logging.command_logger import CommandLogger from bot_data.abc.server_repository_abc import ServerRepositoryABC -from bot_data.model.statistic import Statistic +from bot_data.abc.statistic_repository_abc import StatisticRepositoryABC from modules.permission.abc.permission_service_abc import PermissionServiceABC from modules.stats.service.statistic_service import StatisticService from modules.stats.ui.add_statistic_form import AddStatisticForm -stats = List(Statistic, [ - Statistic( - 'Benutzer XP Aufsteigend', - 'Zeigt XP von jedem Benutzer, aufsteigend nach XP', - textwrap.dedent("""\ - result.header.append('Name') - result.header.append('XP') - - for user in users.order_by(lambda u: u.xp): - row = List(str) - member = guild.get_member(user.discord_id) - row.append(member.name) - row.append(str(user.xp)) - result.values.append(row) - """)), - Statistic('Benutzer XP Absteigend', 'Zeigt XP von jedem Benutzer, absteigend nach XP', '') -]) - class StatsGroup(DiscordCommandABC): @@ -48,6 +29,8 @@ class StatsGroup(DiscordCommandABC): permission_service: PermissionServiceABC, statistic: StatisticService, servers: ServerRepositoryABC, + stats: StatisticRepositoryABC, + db: DatabaseContextABC, ): DiscordCommandABC.__init__(self) @@ -58,6 +41,8 @@ class StatsGroup(DiscordCommandABC): self._permissions = permission_service self._statistic = statistic self._servers = servers + self._stats = stats + self._db = db @commands.hybrid_group() @commands.guild_only() @@ -76,17 +61,27 @@ class StatsGroup(DiscordCommandABC): self._logger.trace(__name__, f'Finished command stats list') return + if ctx.guild is None: + return + embed = discord.Embed( title=self._t.transform('modules.auto_role.list.title'), description=self._t.transform('modules.auto_role.list.description'), color=int('ef9d0d', 16) ) + server = self._servers.get_server_by_discord_id(ctx.guild.id) + stats = self._stats.get_statistics_by_server_id(server.server_id) + + if stats.count() == 0: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.list.nothing_found')) + return + statistics = '' descriptions = '' for statistic in stats: - statistics += f'\n{statistic["Name"]}' - descriptions += f'\n{statistic["Description"]}' + statistics += f'\n{statistic.name}' + descriptions += f'\n{statistic.description}' embed.add_field(name=self._t.transform('modules.stats.list.statistic'), value=statistics, inline=True) embed.add_field(name=self._t.transform('modules.stats.list.description'), value=statistics, inline=True) @@ -105,6 +100,7 @@ class StatsGroup(DiscordCommandABC): try: server = self._servers.get_server_by_discord_id(ctx.guild.id) + stats = self._stats.get_statistics_by_server_id(server.server_id) statistic = stats.where(lambda s: s.name == name).single() result = await self._statistic.execute(statistic.code, server) @@ -130,6 +126,8 @@ class StatsGroup(DiscordCommandABC): @view.autocomplete('name') async def view_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + server = self._servers.get_server_by_discord_id(interaction.guild.id) + stats = self._stats.get_statistics_by_server_id(server.server_id) return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] @stats.command() @@ -144,7 +142,11 @@ class StatsGroup(DiscordCommandABC): self._logger.trace(__name__, f'Finished command stats add') return - form = AddStatisticForm(stats, name, self._message_service, self._logger, self._t) + if ctx.guild is None: + return + + server = self._servers.get_server_by_discord_id(ctx.guild.id) + form = AddStatisticForm(server, self._stats, self._db, name, self._message_service, self._logger, self._t) self._logger.trace(__name__, f'Finished stats add command') self._logger.trace(__name__, f'Started stats command form') await ctx.interaction.response.send_modal(form) @@ -162,8 +164,10 @@ class StatsGroup(DiscordCommandABC): return try: + server = self._servers.get_server_by_discord_id(ctx.guild.id) + stats = self._stats.get_statistics_by_server_id(server.server_id) statistic = stats.where(lambda s: s.name == name).single() - form = AddStatisticForm(stats, name, self._message_service, self._logger, self._t, code=statistic.code, description=statistic.description) + form = AddStatisticForm(server, self._stats, self._db, name, self._message_service, self._logger, self._t, code=statistic.code, description=statistic.description) self._logger.trace(__name__, f'Finished stats edit command') self._logger.trace(__name__, f'Started stats command form') await ctx.interaction.response.send_modal(form) @@ -173,6 +177,8 @@ class StatsGroup(DiscordCommandABC): @edit.autocomplete('name') async def edit_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + server = self._servers.get_server_by_discord_id(interaction.guild.id) + stats = self._stats.get_statistics_by_server_id(server.server_id) return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] @stats.command() @@ -188,12 +194,18 @@ class StatsGroup(DiscordCommandABC): return try: - stats.remove(stats.where(lambda s: s.name == name).single()) + server = self._servers.get_server_by_discord_id(ctx.guild.id) + statistic = self._stats.get_statistic_by_name(name, server.server_id) + self._stats.delete_statistic(statistic) + self._db.save_changes() + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.remove.success')) self._logger.trace(__name__, f'Finished stats remove command') except Exception as e: self._logger.error(__name__, f'Cannot remove statistic {name}', e) - await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.edit.failed')) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.remove.failed')) @remove.autocomplete('name') async def edit_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + server = self._servers.get_server_by_discord_id(interaction.guild.id) + stats = self._stats.get_statistics_by_server_id(server.server_id) return [app_commands.Choice(name=f'{statistic.name}: {statistic.description}', value=statistic.name) for statistic in stats] diff --git a/kdb-bot/src/modules/stats/ui/add_statistic_form.py b/kdb-bot/src/modules/stats/ui/add_statistic_form.py index 073fc9d4..625bac82 100644 --- a/kdb-bot/src/modules/stats/ui/add_statistic_form.py +++ b/kdb-bot/src/modules/stats/ui/add_statistic_form.py @@ -1,11 +1,14 @@ import discord +from cpl_core.database.context import DatabaseContextABC from cpl_query.extension import List from cpl_translation import TranslatePipe from discord import ui, TextStyle from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.logging.command_logger import CommandLogger -from modules.stats.model.statisticmodel import StatisticModel +from bot_data.abc.statistic_repository_abc import StatisticRepositoryABC +from bot_data.model.server import Server +from bot_data.model.statistic import Statistic class AddStatisticForm(ui.Modal): @@ -15,7 +18,9 @@ class AddStatisticForm(ui.Modal): def __init__( self, - stats: List[StatisticModel], + server: Server, + stats: StatisticRepositoryABC, + db: DatabaseContextABC, name: str, message_service: MessageServiceABC, logger: CommandLogger, @@ -25,7 +30,9 @@ class AddStatisticForm(ui.Modal): ): ui.Modal.__init__(self, title=name) + self._server = server self._stats = stats + self._db = db self._name = name self._message_service = message_service self._logger = logger @@ -38,15 +45,26 @@ class AddStatisticForm(ui.Modal): self.description.default = description async def on_submit(self, interaction: discord.Interaction): - statistic = self._stats.where(lambda s: s.name == self._name).single_or_default() + statistic = self._stats.get_statistics_by_server_id(self._server.server_id).where(lambda s: s.name == self._name).single_or_default() + + if interaction.guild is None: + if statistic is None: + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.failed')) + else: + await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.edit.failed')) + return + try: if statistic is None: - self._stats.append(StatisticModel(self._name, self.description.value, self.code.value)) + self._stats.add_statistic(Statistic(self._name, self.description.value, self.code.value, self._server)) + self._db.save_changes() await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.add.success')) return statistic.description = self.description.value statistic.code = self.code.value + self._stats.update_statistic(statistic) + self._db.save_changes() await self._message_service.send_interaction_msg(interaction, self._t.transform('modules.stats.edit.success')) except Exception as e: self._logger.error(__name__, f'Save statistic {self._name} failed', e) From 273548411bfb9d008c8b25aaeaf62e38fe0bf1b4 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 19:57:16 +0100 Subject: [PATCH 162/275] Removed tests #26 --- kdb-bot/src/modules/stats/test/__init__.py | 0 kdb-bot/src/modules/stats/test/user_xp_asc.py | 37 ------------------- .../src/modules/stats/test/user_xp_desc.py | 37 ------------------- 3 files changed, 74 deletions(-) delete mode 100644 kdb-bot/src/modules/stats/test/__init__.py delete mode 100644 kdb-bot/src/modules/stats/test/user_xp_asc.py delete mode 100644 kdb-bot/src/modules/stats/test/user_xp_desc.py diff --git a/kdb-bot/src/modules/stats/test/__init__.py b/kdb-bot/src/modules/stats/test/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/kdb-bot/src/modules/stats/test/user_xp_asc.py b/kdb-bot/src/modules/stats/test/user_xp_asc.py deleted file mode 100644 index 9ee1219e..00000000 --- a/kdb-bot/src/modules/stats/test/user_xp_asc.py +++ /dev/null @@ -1,37 +0,0 @@ -from cpl_discord.container import Guild -from cpl_query.extension import List - -from bot_data.model.auto_role import AutoRole -from bot_data.model.client import Client -from bot_data.model.known_user import KnownUser -from bot_data.model.level import Level -from bot_data.model.server import Server -from bot_data.model.user import User -from bot_data.model.user_joined_server import UserJoinedServer -from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel -from modules.stats.model.statistic_result import StatisticResult - - -async def user_xp_asc( - auto_roles: List[AutoRole], - clients: List[Client], - known_users: List[KnownUser], - levels: List[Level], - servers: List[Server], - user_joined_servers: List[UserJoinedServer], - user_joined_voice_channel: List[UserJoinedVoiceChannel], - users: List[User], - guild: Guild -) -> StatisticResult: - result = StatisticResult() - result.header.append('Name') - result.header.append('XP') - - for user in users.order_by(lambda u: u.xp): - row = List(str) - member = guild.get_member(user.discord_id) - row.append(member.name) - row.append(str(user.xp)) - result.values.append(row) - - return result diff --git a/kdb-bot/src/modules/stats/test/user_xp_desc.py b/kdb-bot/src/modules/stats/test/user_xp_desc.py deleted file mode 100644 index 32cd0f3e..00000000 --- a/kdb-bot/src/modules/stats/test/user_xp_desc.py +++ /dev/null @@ -1,37 +0,0 @@ -from cpl_discord.container import Guild -from cpl_query.extension import List - -from bot_data.model.auto_role import AutoRole -from bot_data.model.client import Client -from bot_data.model.known_user import KnownUser -from bot_data.model.level import Level -from bot_data.model.server import Server -from bot_data.model.user import User -from bot_data.model.user_joined_server import UserJoinedServer -from bot_data.model.user_joined_voice_channel import UserJoinedVoiceChannel -from modules.stats.model.statistic_result import StatisticResult - - -async def user_xp_desc( - auto_roles: List[AutoRole], - clients: List[Client], - known_users: List[KnownUser], - levels: List[Level], - servers: List[Server], - user_joined_servers: List[UserJoinedServer], - user_joined_voice_channel: List[UserJoinedVoiceChannel], - users: List[User], - guild: Guild -) -> StatisticResult: - result = StatisticResult() - result.header.append('Name') - result.header.append('XP') - - for user in users.order_by_descending(lambda u: u.xp): - row = List(str) - member = guild.get_member(user.discord_id) - row.append(member.name) - row.append(str(user.xp)) - result.values.append(row) - - return result From a29e33a3f9e134f47a154f3f552775b8b194675a Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 20:07:37 +0100 Subject: [PATCH 163/275] Readded debug log to level seeder #26 --- kdb-bot/src/modules/level/level_seeder.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py index 7f203d3c..8e6eb9e7 100644 --- a/kdb-bot/src/modules/level/level_seeder.py +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -35,6 +35,7 @@ class LevelSeeder(DataSeederABC): levels = self._levels.find_levels_by_server_id(server.server_id) if levels is None or levels.where(lambda l: l.name == level.name).first_or_default() is None: self._levels.add_level(level) + self._logger.debug(__name__, f'Saved level {level.name}') except discord.errors.Forbidden as e: self._logger.error(__name__, f'Creating level failed', e) level.permissions = 0 From 82365a24ba3e693f6c934d02ef0986f67165a199 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 20:52:58 +0100 Subject: [PATCH 164/275] Fixed stats group #46 --- kdb-bot/src/modules/stats/command/stats_group.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index 96734119..89abcb57 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -84,17 +84,22 @@ class StatsGroup(DiscordCommandABC): descriptions += f'\n{statistic.description}' embed.add_field(name=self._t.transform('modules.stats.list.statistic'), value=statistics, inline=True) - embed.add_field(name=self._t.transform('modules.stats.list.description'), value=statistics, inline=True) + embed.add_field(name=self._t.transform('modules.stats.list.description'), value=descriptions, inline=True) await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait) self._logger.trace(__name__, f'Finished command stats list') @stats.command() @commands.guild_only() async def view(self, ctx: Context, name: str, wait: int = None): - self._logger.debug(__name__, f'Received command stats {ctx}:{name}') + self._logger.debug(__name__, f'Received command stats view {ctx}:{name}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + if not self._permissions.is_member_moderator(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command stats view') + return + if ctx.guild is None: return @@ -122,7 +127,7 @@ class StatsGroup(DiscordCommandABC): self._logger.error(__name__, f'Cannot view statistic {name}', e) await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.stats.view.failed')) - self._logger.trace(__name__, f'Finished stats command') + self._logger.trace(__name__, f'Finished stats view command') @view.autocomplete('name') async def view_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: From 6f3877700e5bbcffd4fc76a3eec0d8022431a493 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 21:40:04 +0100 Subject: [PATCH 165/275] Fixed auto-role output by server #93 --- .../bot_data/abc/auto_role_repository_abc.py | 2 +- .../auto_role/command/auto_role_group.py | 22 +++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/kdb-bot/src/bot_data/abc/auto_role_repository_abc.py b/kdb-bot/src/bot_data/abc/auto_role_repository_abc.py index 899cd486..4283824f 100644 --- a/kdb-bot/src/bot_data/abc/auto_role_repository_abc.py +++ b/kdb-bot/src/bot_data/abc/auto_role_repository_abc.py @@ -22,7 +22,7 @@ class AutoRoleRepositoryABC(ABC): def find_auto_role_by_id(self, id: int) -> Optional[AutoRole]: pass @abstractmethod - def get_auto_roles_by_server_id(self, id: int) -> AutoRole: pass + def get_auto_roles_by_server_id(self, id: int) -> List[AutoRole]: pass @abstractmethod def get_auto_role_by_message_id(self, id: int) -> AutoRole: pass diff --git a/kdb-bot/src/modules/auto_role/command/auto_role_group.py b/kdb-bot/src/modules/auto_role/command/auto_role_group.py index 515d2fe4..25ba7036 100644 --- a/kdb-bot/src/modules/auto_role/command/auto_role_group.py +++ b/kdb-bot/src/modules/auto_role/command/auto_role_group.py @@ -66,12 +66,16 @@ class AutoRoleGroup(DiscordCommandABC): self._logger.trace(__name__, f'Finished command auto-role list') return + if ctx.guild is None: + return + embed = discord.Embed( title=self._t.transform('modules.auto_role.list.title'), description=self._t.transform('modules.auto_role.list.description'), color=int('ef9d0d', 16) ) - auto_roles = self._auto_roles.get_auto_roles() + server = self._servers.get_server_by_discord_id(ctx.guild.id) + auto_roles = self._auto_roles.get_auto_roles_by_server_id(server.server_id) if auto_roles.count() < 1: await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.error.nothing_found')) self._logger.trace(__name__, f'Finished command auto-role list') @@ -165,7 +169,8 @@ class AutoRoleGroup(DiscordCommandABC): @remove.autocomplete('auto_role') async def remove_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: - auto_roles = self._auto_roles.get_auto_roles().select(lambda x: x.auto_role_id) + server = self._servers.get_server_by_discord_id(interaction.guild.id) + auto_roles = self._auto_roles.get_auto_roles_by_server_id(server.server_id).select(lambda x: x.auto_role_id) return [app_commands.Choice(name=auto_role, value=auto_role) for auto_role in auto_roles] @auto_role.group() @@ -212,7 +217,8 @@ class AutoRoleGroup(DiscordCommandABC): @list.autocomplete('auto_role') async def list_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: - auto_roles = self._auto_roles.get_auto_roles().select(lambda x: x.auto_role_id) + server = self._servers.get_server_by_discord_id(interaction.guild.id) + auto_roles = self._auto_roles.get_auto_roles_by_server_id(server.server_id).select(lambda x: x.auto_role_id) return [app_commands.Choice(name=auto_role, value=auto_role) for auto_role in auto_roles] @rule.command() @@ -259,7 +265,8 @@ class AutoRoleGroup(DiscordCommandABC): @add.autocomplete('auto_role') async def add_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: - auto_roles = self._auto_roles.get_auto_roles().select(lambda x: x.auto_role_id) + server = self._servers.get_server_by_discord_id(interaction.guild.id) + auto_roles = self._auto_roles.get_auto_roles_by_server_id(server.server_id).select(lambda x: x.auto_role_id) return [app_commands.Choice(name=auto_role, value=auto_role) for auto_role in auto_roles] @add.autocomplete('emoji_name') @@ -299,6 +306,7 @@ class AutoRoleGroup(DiscordCommandABC): @remove.autocomplete('auto_role_rule') async def remove_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: - rules = self._auto_roles.get_auto_role_rules() - return [app_commands.Choice(name=f'{rule.auto_role_rule_id} {rule.emoji_name} {interaction.guild.get_role(int(rule.role_id))}', value=rule.auto_role_rule_id) for rule in - rules] + server = self._servers.get_server_by_discord_id(interaction.guild.id) + auto_roles = self._auto_roles.get_auto_roles_by_server_id(server.server_id).select(lambda x: x.auto_role_id) + rules = auto_roles.select_many(lambda ar: self._auto_roles.get_auto_role_rules_by_auto_role_id(ar)) + return [app_commands.Choice(name=f'{rule.auto_role_rule_id} {rule.emoji_name} {interaction.guild.get_role(int(rule.role_id))}', value=rule.auto_role_rule_id) for rule in rules] From e47318d8e9236f83d68a8fbc17901f5abcc33493 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 22:21:32 +0100 Subject: [PATCH 166/275] Added received command to new commands #105 --- .../src/modules/auto_role/command/auto_role_group.py | 6 ++++++ kdb-bot/src/modules/base/command/purge_command.py | 1 - kdb-bot/src/modules/base/command/restart_command.py | 1 + kdb-bot/src/modules/base/command/shutdown_command.py | 1 + kdb-bot/src/modules/level/command/level_group.py | 12 ++++++++---- kdb-bot/src/modules/stats/command/stats_group.py | 5 +++++ 6 files changed, 21 insertions(+), 5 deletions(-) diff --git a/kdb-bot/src/modules/auto_role/command/auto_role_group.py b/kdb-bot/src/modules/auto_role/command/auto_role_group.py index 515d2fe4..62667069 100644 --- a/kdb-bot/src/modules/auto_role/command/auto_role_group.py +++ b/kdb-bot/src/modules/auto_role/command/auto_role_group.py @@ -60,6 +60,7 @@ class AutoRoleGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command auto-role list {ctx}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_moderator(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) @@ -94,6 +95,7 @@ class AutoRoleGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command auto-role add {ctx} {message_id}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_moderator(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) @@ -140,6 +142,7 @@ class AutoRoleGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command auto-role remove {ctx} {auto_role}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_moderator(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) @@ -179,6 +182,7 @@ class AutoRoleGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command auto-role rule list {ctx}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_moderator(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) @@ -221,6 +225,7 @@ class AutoRoleGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command auto-role add {ctx} {auto_role}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_moderator(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) @@ -278,6 +283,7 @@ class AutoRoleGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command auto-role remove {ctx} {auto_role_rule}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_moderator(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) diff --git a/kdb-bot/src/modules/base/command/purge_command.py b/kdb-bot/src/modules/base/command/purge_command.py index 6599f59a..11b90c22 100644 --- a/kdb-bot/src/modules/base/command/purge_command.py +++ b/kdb-bot/src/modules/base/command/purge_command.py @@ -41,7 +41,6 @@ class PurgeCommand(DiscordCommandABC): self._logger.debug(__name__, f'Received command purge {ctx}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return - self._client_utils.received_command(ctx.guild.id) server_settings: ServerSettings = self._config.get_configuration(f'ServerSettings_{ctx.guild.id}') diff --git a/kdb-bot/src/modules/base/command/restart_command.py b/kdb-bot/src/modules/base/command/restart_command.py index 179eeceb..56c48958 100644 --- a/kdb-bot/src/modules/base/command/restart_command.py +++ b/kdb-bot/src/modules/base/command/restart_command.py @@ -47,6 +47,7 @@ class RestartCommand(DiscordCommandABC): self._logger.debug(__name__, f'Received command restart {ctx}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) self._client_utils.received_command(ctx.guild.id) diff --git a/kdb-bot/src/modules/base/command/shutdown_command.py b/kdb-bot/src/modules/base/command/shutdown_command.py index 0fe0f5fb..e2541e8d 100644 --- a/kdb-bot/src/modules/base/command/shutdown_command.py +++ b/kdb-bot/src/modules/base/command/shutdown_command.py @@ -47,6 +47,7 @@ class ShutdownCommand(DiscordCommandABC): self._logger.debug(__name__, f'Received command shutdown {ctx}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) self._client_utils.received_command(ctx.guild.id) diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 32ccb708..7a49593c 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -78,6 +78,7 @@ class LevelGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command level list {ctx}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_moderator(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) @@ -117,6 +118,9 @@ class LevelGroup(DiscordCommandABC): @commands.guild_only() async def create(self, ctx: Context, name: str, color: str, min_xp: int, permissions: int): self._logger.debug(__name__, f'Received command level create {ctx}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + self._client_utils.received_command(ctx.guild.id) try: color = hex(discord.Colour.from_str(color).value) @@ -130,9 +134,6 @@ class LevelGroup(DiscordCommandABC): self._logger.error(__name__, f'Error parsing permissions {permissions}', e) return - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - if ctx.guild is None: return @@ -194,9 +195,9 @@ class LevelGroup(DiscordCommandABC): @commands.guild_only() async def remove(self, ctx: Context, level: str): self._logger.debug(__name__, f'Received command level remove {ctx}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_admin(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) @@ -243,6 +244,7 @@ class LevelGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command level down {ctx} {member}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_moderator(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) @@ -284,6 +286,7 @@ class LevelGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command level up {ctx} {member}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_moderator(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) @@ -325,6 +328,7 @@ class LevelGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command level up {ctx} {member}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_moderator(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index 89abcb57..41d40c5e 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -55,6 +55,7 @@ class StatsGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command stats list {ctx}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_moderator(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) @@ -94,6 +95,7 @@ class StatsGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command stats view {ctx}:{name}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_moderator(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) @@ -141,6 +143,7 @@ class StatsGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command stats add {ctx}: {name}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_technician(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) @@ -162,6 +165,7 @@ class StatsGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command stats edit {ctx}: {name}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_technician(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) @@ -192,6 +196,7 @@ class StatsGroup(DiscordCommandABC): self._logger.debug(__name__, f'Received command stats remove {ctx}: {name}') if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): return + self._client_utils.received_command(ctx.guild.id) if not self._permissions.is_member_technician(ctx.author): await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) From 356dc1848c4518e5e3b48095663cc2865e4aa575 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 22:34:25 +0100 Subject: [PATCH 167/275] Added ontime to user info #97 --- kdb-bot/src/bot/translation/de.json | 1 + kdb-bot/src/bot_data/abc/user_joined_voice_channel_abc.py | 2 +- kdb-bot/src/modules/base/command/user_group.py | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 606e89c6..fc7e5631 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -162,6 +162,7 @@ "discord_join": "Discord beigetreten am", "last_join": "Server beigetreten am", "xp": "XP", + "ontime": "Ontime", "roles": "Rollen", "joins": "Beitritte", "lefts": "Abgänge", diff --git a/kdb-bot/src/bot_data/abc/user_joined_voice_channel_abc.py b/kdb-bot/src/bot_data/abc/user_joined_voice_channel_abc.py index a9228c48..d46a6f2b 100644 --- a/kdb-bot/src/bot_data/abc/user_joined_voice_channel_abc.py +++ b/kdb-bot/src/bot_data/abc/user_joined_voice_channel_abc.py @@ -16,7 +16,7 @@ class UserJoinedVoiceChannelRepositoryABC(ABC): def get_user_joined_voice_channel_by_id(self, id: int) -> UserJoinedVoiceChannel: pass @abstractmethod - def get_user_joined_voice_channels_by_user_id(self, user_id: int) -> list[UserJoinedVoiceChannel]: pass + def get_user_joined_voice_channels_by_user_id(self, user_id: int) -> List[UserJoinedVoiceChannel]: pass @abstractmethod def get_active_user_joined_voice_channel_by_user_id(self, user_id: int) -> UserJoinedVoiceChannel: pass diff --git a/kdb-bot/src/modules/base/command/user_group.py b/kdb-bot/src/modules/base/command/user_group.py index 1988902b..a2efb8ba 100644 --- a/kdb-bot/src/modules/base/command/user_group.py +++ b/kdb-bot/src/modules/base/command/user_group.py @@ -14,6 +14,7 @@ from bot_core.logging.command_logger import CommandLogger from bot_core.pipes.date_time_offset_pipe import DateTimeOffsetPipe from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC +from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -31,6 +32,7 @@ class UserGroup(DiscordCommandABC): servers: ServerRepositoryABC, users: UserRepositoryABC, user_joined_servers: UserJoinedServerRepositoryABC, + user_joined_voice_channel: UserJoinedVoiceChannelRepositoryABC, translate: TranslatePipe, date: DateTimeOffsetPipe ): @@ -45,6 +47,7 @@ class UserGroup(DiscordCommandABC): self._servers = servers self._users = users self._user_joined_servers = user_joined_servers + self._user_joined_voice_channel = user_joined_voice_channel self._t = translate self._date = date @@ -81,11 +84,15 @@ class UserGroup(DiscordCommandABC): color=int('ef9d0d', 16) ) + ujvs = self._user_joined_voice_channel.get_user_joined_voice_channels_by_user_id(user.user_id) + ontime = ujvs.sum(lambda join: round((join.leaved_on - join.joined_on).total_seconds() / 3600, 2)) + embed.add_field(name=self._t.transform('modules.base.user_info.fields.id'), value=member.id) embed.add_field(name=self._t.transform('modules.base.user_info.fields.name'), value=member.name) embed.add_field(name=self._t.transform('modules.base.user_info.fields.discord_join'), value=self._date.transform(member.created_at), inline=False) embed.add_field(name=self._t.transform('modules.base.user_info.fields.last_join'), value=self._date.transform(member.joined_at), inline=False) embed.add_field(name=self._t.transform('modules.base.user_info.fields.xp'), value=str(user.xp)) + embed.add_field(name=self._t.transform('modules.base.user_info.fields.ontime'), value=str(ontime)) roles = '' for role in member.roles: From 762e85d062ec205348bc1ed62ea4a1cd783a3a73 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 10 Nov 2022 07:34:03 +0100 Subject: [PATCH 168/275] Removed configs --- .gitmodules | 6 + kdb-bot/LICENSE | 9 ++ kdb-bot/README.md | 2 + kdb-bot/src/bot/config | 1 + .../src/bot/config/appsettings.PC-Nick.json | 86 ------------- .../bot/config/appsettings.development.json | 100 --------------- .../bot/config/appsettings.edrafts-lapi.json | 97 -------------- .../bot/config/appsettings.edrafts-pc.json | 97 -------------- .../src/bot/config/appsettings.example.json | 18 --- kdb-bot/src/bot/config/appsettings.json | 34 ----- .../bot/config/appsettings.production.json | 120 ------------------ .../src/bot/config/appsettings.staging.json | 96 -------------- kdb-bot/src/bot/config/feature-flags.json | 16 --- kdb-bot/src/bot_api/config | 1 + .../config/apisettings.development.json | 33 ----- .../config/apisettings.edrafts-lapi.json | 28 ---- .../config/apisettings.edrafts-pc.json | 27 ---- kdb-bot/src/bot_api/config/apisettings.json | 1 - .../config/apisettings.production.json | 33 ----- .../bot_api/config/apisettings.staging.json | 33 ----- .../bot_api/config/appsettings.PC-Nick.json | 1 - 21 files changed, 19 insertions(+), 820 deletions(-) create mode 100644 .gitmodules create mode 100644 kdb-bot/LICENSE create mode 100644 kdb-bot/README.md create mode 160000 kdb-bot/src/bot/config delete mode 100644 kdb-bot/src/bot/config/appsettings.PC-Nick.json delete mode 100644 kdb-bot/src/bot/config/appsettings.development.json delete mode 100644 kdb-bot/src/bot/config/appsettings.edrafts-lapi.json delete mode 100644 kdb-bot/src/bot/config/appsettings.edrafts-pc.json delete mode 100644 kdb-bot/src/bot/config/appsettings.example.json delete mode 100644 kdb-bot/src/bot/config/appsettings.json delete mode 100644 kdb-bot/src/bot/config/appsettings.production.json delete mode 100644 kdb-bot/src/bot/config/appsettings.staging.json delete mode 100644 kdb-bot/src/bot/config/feature-flags.json create mode 160000 kdb-bot/src/bot_api/config delete mode 100644 kdb-bot/src/bot_api/config/apisettings.development.json delete mode 100644 kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json delete mode 100644 kdb-bot/src/bot_api/config/apisettings.edrafts-pc.json delete mode 100644 kdb-bot/src/bot_api/config/apisettings.json delete mode 100644 kdb-bot/src/bot_api/config/apisettings.production.json delete mode 100644 kdb-bot/src/bot_api/config/apisettings.staging.json delete mode 100644 kdb-bot/src/bot_api/config/appsettings.PC-Nick.json diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..1b416f60 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "kdb-bot/src/bot/config"] + path = kdb-bot/src/bot/config + url = https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot.config.git +[submodule "kdb-bot/src/bot_api/config"] + path = kdb-bot/src/bot_api/config + url = https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot.api.config.git diff --git a/kdb-bot/LICENSE b/kdb-bot/LICENSE new file mode 100644 index 00000000..374f2e3a --- /dev/null +++ b/kdb-bot/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2022-2023 sh-edraft.de + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/kdb-bot/README.md b/kdb-bot/README.md new file mode 100644 index 00000000..72b3dd33 --- /dev/null +++ b/kdb-bot/README.md @@ -0,0 +1,2 @@ +# kd_discord_bot + diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config new file mode 160000 index 00000000..fb3afa7e --- /dev/null +++ b/kdb-bot/src/bot/config @@ -0,0 +1 @@ +Subproject commit fb3afa7e74da374270843eae1850ff6b60819600 diff --git a/kdb-bot/src/bot/config/appsettings.PC-Nick.json b/kdb-bot/src/bot/config/appsettings.PC-Nick.json deleted file mode 100644 index d296c19c..00000000 --- a/kdb-bot/src/bot/config/appsettings.PC-Nick.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "TimeFormatSettings": { - "DateFormat": "%Y-%m-%d", - "TimeFormat": "%H:%M:%S", - "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", - "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" - }, - "LoggingSettings": { - "Path": "logs/", - "Filename": "bot.log", - "ConsoleLogLevel": "TRACE", - "FileLogLevel": "TRACE" - }, - "BotLoggingSettings": { - "Command": { - "Path": "logs/", - "Filename": "commands.log", - "ConsoleLogLevel": "DEBUG", - "FileLogLevel": "TRACE" - }, - "Database": { - "Path": "logs/", - "Filename": "database.log", - "ConsoleLogLevel": "DEBUG", - "FileLogLevel": "TRACE" - }, - "Message": { - "Path": "logs/", - "Filename": "message.log", - "ConsoleLogLevel": "DEBUG", - "FileLogLevel": "TRACE" - } - }, - "DatabaseSettings": { - "Host": "localhost", - "User": "root", - "Password": "MTAwNjE5OTdOaWNrLko=", - "Database": "kd_kdb", - "Charset": "utf8mb4", - "UseUnicode": "true", - "Buffered": "true", - "AuthPlugin": "mysql_native_password" - }, - "DiscordBot": { - "Token": "MTAyOTgxMjE0Mjk4NTE5MTYxNA.G4ArAJ.sZh6pE-mwO2qDAr1mfHEoo7EwbJb-TZT8h6nGg", - "Prefix": "!kn " - }, - "Bot": { - "910199451145076828": { - "MessageDeleteTimer": 2 - }, - "Technicians": [ - 240160344557879316 - ], - "WaitForRestart": 4, - "WaitForShutdown": 4 - }, - "Base": { - "910199451145076828": { - "MaxVoiceStateHours": 24, - "XpPerMessage": 2, - "XpPerOntimeHour": 4, - "AFKChannelIds": [ - 910199452915093593, - 910199452915093594 - ], - "AFKCommandChannelId": 910199452915093594, - "HelpCommandReferenceUrl": "https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot/wiki/Befehle" - } - }, - "BootLog": { - "910199451145076828": { - "LoginMessageChannelId": "910199452915093588" - } - }, - "Permission": { - "910199451145076828": { - "AdminRoleIds": [ - 925072155203477584 - ], - "ModeratorRoleIds": [ - 925072209884635167 - ] - } - } -} diff --git a/kdb-bot/src/bot/config/appsettings.development.json b/kdb-bot/src/bot/config/appsettings.development.json deleted file mode 100644 index a69ebd74..00000000 --- a/kdb-bot/src/bot/config/appsettings.development.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "TimeFormatSettings": { - "DateFormat": "%Y-%m-%d", - "TimeFormat": "%H:%M:%S", - "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", - "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" - }, - "LoggingSettings": { - "Path": "logs/$date_now/", - "Filename": "bot.log", - "ConsoleLogLevel": "TRACE", - "FileLogLevel": "TRACE" - }, - "BotLoggingSettings": { - "Api": { - "Path": "logs/$date_now/", - "Filename": "api.log", - "ConsoleLogLevel": "TRACE", - "FileLogLevel": "TRACE" - }, - "Command": { - "Path": "logs/$date_now/", - "Filename": "commands.log", - "ConsoleLogLevel": "TRACE", - "FileLogLevel": "TRACE" - }, - "Database": { - "Path": "logs/$date_now/", - "Filename": "database.log", - "ConsoleLogLevel": "DEBUG", - "FileLogLevel": "TRACE" - }, - "Message": { - "Path": "logs/$date_now/", - "Filename": "message.log", - "ConsoleLogLevel": "TRACE", - "FileLogLevel": "TRACE" - } - }, - "DiscordBot": { - "Token": "OTk4MTU5NjczODkzMDYwNzM4.GN3QyA.yvWO6L7Eu36gXQ7ARDs0Jg2J1VqIDnHLou5lT4", - "Prefix": "!kd " - }, - "Translation": { - "DefaultLanguage": "de", - "Languages": [ - "de" - ] - }, - "DatabaseSettings": { - "Host": "kdb_db_dev_1", - "User": "kd_kdb", - "Password": "IXc1RF8mc3RlQ2VqZmpxfmJ7MF9EUEBlOsKnWDI/SlV6", - "Database": "kd_kdb", - "Port": "3306", - "Charset": "utf8mb4", - "UseUnicode": "true", - "Buffered": "true", - "AuthPlugin": "mysql_native_password" - }, - "Bot": { - "910199451145076828": { - "MessageDeleteTimer": 4 - }, - "Technicians": [ - 240160344557879316, - 236592458664902657 - ], - "WaitForRestart": 4, - "WaitForShutdown": 4 - }, - "Base": { - "910199451145076828": { - "MaxVoiceStateHours": 24, - "XpPerMessage": 2, - "XpPerOntimeHour": 4, - "AFKChannelIds": [ - 910199452915093593, - 910199452915093594 - ], - "AFKCommandChannelId": 910199452915093594, - "HelpCommandReferenceUrl": "https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot/wiki/Befehle" - } - }, - "BootLog": { - "910199451145076828": { - "LoginMessageChannelId": "910199452915093588" - } - }, - "Permission": { - "910199451145076828": { - "AdminRoleIds": [ - 925072155203477584 - ], - "ModeratorRoleIds": [ - 925072209884635167 - ] - } - } -} \ No newline at end of file diff --git a/kdb-bot/src/bot/config/appsettings.edrafts-lapi.json b/kdb-bot/src/bot/config/appsettings.edrafts-lapi.json deleted file mode 100644 index e912b148..00000000 --- a/kdb-bot/src/bot/config/appsettings.edrafts-lapi.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "TimeFormatSettings": { - "DateFormat": "%Y-%m-%d", - "TimeFormat": "%H:%M:%S", - "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", - "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" - }, - "LoggingSettings": { - "Path": "logs/", - "Filename": "bot.log", - "ConsoleLogLevel": "DEBUG", - "FileLogLevel": "TRACE" - }, - "BotLoggingSettings": { - "Api": { - "Path": "logs/", - "Filename": "api.log", - "ConsoleLogLevel": "TRACE", - "FileLogLevel": "TRACE" - }, - "Command": { - "Path": "logs/", - "Filename": "commands.log", - "ConsoleLogLevel": "DEBUG", - "FileLogLevel": "TRACE" - }, - "Database": { - "Path": "logs/", - "Filename": "database.log", - "ConsoleLogLevel": "DEBUG", - "FileLogLevel": "TRACE" - }, - "Message": { - "Path": "logs/", - "Filename": "message.log", - "ConsoleLogLevel": "DEBUG", - "FileLogLevel": "TRACE" - } - }, - "DatabaseSettings": { - "Host": "localhost", - "User": "kd_kdb", - "Password": "VGpZcihrb0N2T2MyZUlURQ==", - "Database": "keksdose_bot_dev", - "Charset": "utf8mb4", - "UseUnicode": "true", - "Buffered": "true", - "AuthPlugin": "mysql_native_password" - }, - "DiscordBot": { - "Token": "OTk4MTYwNDI3Njg5MTgxMjM3.GI7h67.BqD6Lu1Tz0MuG8iktYrcLnHi1pNozyMiWFGTKI", - "Prefix": "!ke " - }, - "Bot": { - "910199451145076828": { - "MessageDeleteTimer": 2 - }, - "Technicians": [ - 240160344557879316 - ], - "WaitForRestart": 4, - "WaitForShutdown": 4 - }, - "Base": { - "910199451145076828": { - "MaxVoiceStateHours": 24, - "XpPerMessage": 2, - "XpPerOntimeHour": 4, - "AFKChannelIds": [ - 910199452915093593, - 910199452915093594 - ], - "AFKCommandChannelId": 910199452915093594, - "HelpCommandReferenceUrl": "https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot/wiki/Befehle" - } - }, - "BootLog": { - "910199451145076828": { - "LoginMessageChannelId": 910199452915093588 - } - }, - "Level": { - "910199451145076828": { - "ChangedLevelNotificationChannelId": 910199452667637892 - } - }, - "Permission": { - "910199451145076828": { - "AdminRoleIds": [ - 925072155203477584 - ], - "ModeratorRoleIds": [ - 925072209884635167 - ] - } - } -} diff --git a/kdb-bot/src/bot/config/appsettings.edrafts-pc.json b/kdb-bot/src/bot/config/appsettings.edrafts-pc.json deleted file mode 100644 index e912b148..00000000 --- a/kdb-bot/src/bot/config/appsettings.edrafts-pc.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "TimeFormatSettings": { - "DateFormat": "%Y-%m-%d", - "TimeFormat": "%H:%M:%S", - "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", - "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" - }, - "LoggingSettings": { - "Path": "logs/", - "Filename": "bot.log", - "ConsoleLogLevel": "DEBUG", - "FileLogLevel": "TRACE" - }, - "BotLoggingSettings": { - "Api": { - "Path": "logs/", - "Filename": "api.log", - "ConsoleLogLevel": "TRACE", - "FileLogLevel": "TRACE" - }, - "Command": { - "Path": "logs/", - "Filename": "commands.log", - "ConsoleLogLevel": "DEBUG", - "FileLogLevel": "TRACE" - }, - "Database": { - "Path": "logs/", - "Filename": "database.log", - "ConsoleLogLevel": "DEBUG", - "FileLogLevel": "TRACE" - }, - "Message": { - "Path": "logs/", - "Filename": "message.log", - "ConsoleLogLevel": "DEBUG", - "FileLogLevel": "TRACE" - } - }, - "DatabaseSettings": { - "Host": "localhost", - "User": "kd_kdb", - "Password": "VGpZcihrb0N2T2MyZUlURQ==", - "Database": "keksdose_bot_dev", - "Charset": "utf8mb4", - "UseUnicode": "true", - "Buffered": "true", - "AuthPlugin": "mysql_native_password" - }, - "DiscordBot": { - "Token": "OTk4MTYwNDI3Njg5MTgxMjM3.GI7h67.BqD6Lu1Tz0MuG8iktYrcLnHi1pNozyMiWFGTKI", - "Prefix": "!ke " - }, - "Bot": { - "910199451145076828": { - "MessageDeleteTimer": 2 - }, - "Technicians": [ - 240160344557879316 - ], - "WaitForRestart": 4, - "WaitForShutdown": 4 - }, - "Base": { - "910199451145076828": { - "MaxVoiceStateHours": 24, - "XpPerMessage": 2, - "XpPerOntimeHour": 4, - "AFKChannelIds": [ - 910199452915093593, - 910199452915093594 - ], - "AFKCommandChannelId": 910199452915093594, - "HelpCommandReferenceUrl": "https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot/wiki/Befehle" - } - }, - "BootLog": { - "910199451145076828": { - "LoginMessageChannelId": 910199452915093588 - } - }, - "Level": { - "910199451145076828": { - "ChangedLevelNotificationChannelId": 910199452667637892 - } - }, - "Permission": { - "910199451145076828": { - "AdminRoleIds": [ - 925072155203477584 - ], - "ModeratorRoleIds": [ - 925072209884635167 - ] - } - } -} diff --git a/kdb-bot/src/bot/config/appsettings.example.json b/kdb-bot/src/bot/config/appsettings.example.json deleted file mode 100644 index 4242ee40..00000000 --- a/kdb-bot/src/bot/config/appsettings.example.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "TimeFormatSettings": { - "DateFormat": "%Y-%m-%d", - "TimeFormat": "%H:%M:%S", - "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", - "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" - }, - "LoggingSettings": { - "Path": "logs/", - "Filename": "log_$start_time.log", - "ConsoleLogLevel": "ERROR", - "FileLogLevel": "WARN" - }, - "DiscordBot": { - "Token": "", - "Prefix": "! " - } -} \ No newline at end of file diff --git a/kdb-bot/src/bot/config/appsettings.json b/kdb-bot/src/bot/config/appsettings.json deleted file mode 100644 index 643f38ab..00000000 --- a/kdb-bot/src/bot/config/appsettings.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "TimeFormatSettings": { - "DateFormat": "%Y-%m-%d", - "TimeFormat": "%H:%M:%S", - "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", - "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" - }, - "LoggingSettings": { - "Path": "logs/$date_now/", - "Filename": "bot.log", - "ConsoleLogLevel": "ERROR", - "FileLogLevel": "WARN" - }, - "BotLoggingSettings": { - "Command": { - "Path": "logs/$date_now/", - "Filename": "commands.log", - "ConsoleLogLevel": "ERROR", - "FileLogLevel": "WARN" - }, - "Database": { - "Path": "logs/$date_now/", - "Filename": "database.log", - "ConsoleLogLevel": "ERROR", - "FileLogLevel": "WARN" - }, - "Message": { - "Path": "logs/$date_now/", - "Filename": "message.log", - "ConsoleLogLevel": "ERROR", - "FileLogLevel": "WARN" - } - } -} \ No newline at end of file diff --git a/kdb-bot/src/bot/config/appsettings.production.json b/kdb-bot/src/bot/config/appsettings.production.json deleted file mode 100644 index 0b1ba8c6..00000000 --- a/kdb-bot/src/bot/config/appsettings.production.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "TimeFormatSettings": { - "DateFormat": "%Y-%m-%d", - "TimeFormat": "%H:%M:%S", - "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", - "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" - }, - "LoggingSettings": { - "Path": "logs/$date_now/", - "Filename": "bot.log", - "ConsoleLogLevel": "ERROR", - "FileLogLevel": "INFO" - }, - "BotLoggingSettings": { - "Api": { - "Path": "logs/$date_now/", - "Filename": "api.log", - "ConsoleLogLevel": "ERROR", - "FileLogLevel": "INFO" - }, - "Command": { - "Path": "logs/$date_now/", - "Filename": "commands.log", - "ConsoleLogLevel": "ERROR", - "FileLogLevel": "INFO" - }, - "Database": { - "Path": "logs/$date_now/", - "Filename": "database.log", - "ConsoleLogLevel": "ERROR", - "FileLogLevel": "INFO" - }, - "Message": { - "Path": "logs/$date_now/", - "Filename": "message.log", - "ConsoleLogLevel": "ERROR", - "FileLogLevel": "INFO" - } - }, - "Translation": { - "DefaultLanguage": "de", - "Languages": [ - "de" - ] - }, - "DatabaseSettings": { - "Host": "kdb_db_prod_1", - "User": "kd_kdb", - "Password": "Kz1nan0ow4RFYlJHNl9TJsO2fcO8PnphTlQ9ckV7X35tPHk=", - "Database": "kd_kdb", - "Port": "3306", - "Charset": "utf8mb4", - "UseUnicode": "true", - "Buffered": "true", - "AuthPlugin": "mysql_native_password" - }, - "Bot": { - "650366049023295514": { - "MessageDeleteTimer": 2 - }, - "910199451145076828": { - "MessageDeleteTimer": 2 - }, - "Technicians": [ - 240160344557879316, - 236592458664902657 - ], - "WaitForRestart": 4, - "WaitForShutdown": 4 - }, - "Base": { - "650366049023295514": { - "MaxVoiceStateHours": 24, - "XpPerMessage": 2, - "XpPerOntimeHour": 4, - "AFKChannelIds": [ - 784530469290246145 - ], - "AFKCommandChannelId": 784530469290246145, - "HelpCommandReferenceUrl": "https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot/wiki/Befehle" - }, - "910199451145076828": { - "MaxVoiceStateHours": 24, - "XpPerMessage": 2, - "XpPerOntimeHour": 4, - "AFKChannelIds": [ - 910199452915093593, - 910199452915093594 - ], - "AFKCommandChannelId": 910199452915093594, - "HelpCommandReferenceUrl": "https://docs.sh-edraft.de/kdb/" - } - }, - "BootLog": { - "650366049023295514": { - "LoginMessageChannelId": "998544927094997093" - }, - "910199451145076828": { - "LoginMessageChannelId": "910199452915093588" - } - }, - "Permission": { - "650366049023295514": { - "AdminRoleIds": [ - 666129320481128479 - ], - "ModeratorRoleIds": [ - 998161580669804565 - ] - }, - "910199451145076828": { - "AdminRoleIds": [ - 925072155203477584 - ], - "ModeratorRoleIds": [ - 925072209884635167 - ] - } - } -} \ No newline at end of file diff --git a/kdb-bot/src/bot/config/appsettings.staging.json b/kdb-bot/src/bot/config/appsettings.staging.json deleted file mode 100644 index d467b0da..00000000 --- a/kdb-bot/src/bot/config/appsettings.staging.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "TimeFormatSettings": { - "DateFormat": "%Y-%m-%d", - "TimeFormat": "%H:%M:%S", - "DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f", - "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" - }, - "LoggingSettings": { - "Path": "logs/$date_now/", - "Filename": "bot.log", - "ConsoleLogLevel": "INFO", - "FileLogLevel": "DEBUG" - }, - "BotLoggingSettings": { - "Api": { - "Path": "logs/$date_now/", - "Filename": "api.log", - "ConsoleLogLevel": "INFO", - "FileLogLevel": "DEBUG" - }, - "Command": { - "Path": "logs/$date_now/", - "Filename": "commands.log", - "ConsoleLogLevel": "INFO", - "FileLogLevel": "DEBUG" - }, - "Database": { - "Path": "logs/$date_now/", - "Filename": "database.log", - "ConsoleLogLevel": "INFO", - "FileLogLevel": "DEBUG" - }, - "Message": { - "Path": "logs/$date_now/", - "Filename": "message.log", - "ConsoleLogLevel": "INFO", - "FileLogLevel": "DEBUG" - } - }, - "Translation": { - "DefaultLanguage": "de", - "Languages": [ - "de" - ] - }, - "DatabaseSettings": { - "Host": "kdb_db_staging_1", - "User": "kd_kdb", - "Password": "MyZZd1ZNQ3fDvGI9TFV0N0LCp8OWc1k/S3J+WFJ0RCMmJmY=", - "Database": "kd_kdb", - "Port": "3306", - "Charset": "utf8mb4", - "UseUnicode": "true", - "Buffered": "true", - "AuthPlugin": "mysql_native_password" - }, - "Bot": { - "910199451145076828": { - "MessageDeleteTimer": 4 - }, - "Technicians": [ - 240160344557879316, - 236592458664902657 - ], - "WaitForRestart": 4, - "WaitForShutdown": 4 - }, - "Base": { - "910199451145076828": { - "MaxVoiceStateHours": 24, - "XpPerMessage": 2, - "XpPerOntimeHour": 4, - "AFKChannelIds": [ - 910199452915093593, - 910199452915093594 - ], - "AFKCommandChannelId": 910199452915093594, - "HelpCommandReferenceUrl": "https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot/wiki/Befehle" - } - }, - "BootLog": { - "910199451145076828": { - "LoginMessageChannelId": "910199452915093588" - } - }, - "Permission": { - "910199451145076828": { - "AdminRoleIds": [ - 925072155203477584 - ], - "ModeratorRoleIds": [ - 925072209884635167 - ] - } - } -} \ No newline at end of file diff --git a/kdb-bot/src/bot/config/feature-flags.json b/kdb-bot/src/bot/config/feature-flags.json deleted file mode 100644 index da07fa23..00000000 --- a/kdb-bot/src/bot/config/feature-flags.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "FeatureFlags": { - "ApiModule": false, - "AdminModule": true, - "AutoRoleModule": true, - "BaseModule": true, - "BootLogModule": true, - "CoreModule": true, - "CoreExtensionModule": true, - "DatabaseModule": true, - "ModeratorModule": true, - "LevelModule": true, - "PermissionModule": true, - "PresenceModule": true - } -} diff --git a/kdb-bot/src/bot_api/config b/kdb-bot/src/bot_api/config new file mode 160000 index 00000000..90197e75 --- /dev/null +++ b/kdb-bot/src/bot_api/config @@ -0,0 +1 @@ +Subproject commit 90197e75e535a74caee41b4f39fd459ffb1891b5 diff --git a/kdb-bot/src/bot_api/config/apisettings.development.json b/kdb-bot/src/bot_api/config/apisettings.development.json deleted file mode 100644 index 41cb1f57..00000000 --- a/kdb-bot/src/bot_api/config/apisettings.development.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "Api": { - "Port": 80, - "Host": "0.0.0.0", - "RedirectToHTTPS": false - }, - "Authentication": { - "SecretKey": "RjNiNUxEeisjSnZ6Zz1XIUBnc2EleHNG", - "Issuer": "http://localhost:8044", - "Audience": "http://localhost:8043", - "TokenExpireTime": 1, - "RefreshTokenExpireTime": 7 - }, - "DiscordAuthentication": { - "ClientSecret": "cmhqYmF4MXBCd2IzeEZoSXRZQ29vY3NwUWwxQzFTZng=", - "RedirectURL": "http://localhost:8043/auth/register", - "Scope": [ - "identify", - "email" - ], - "TokenURL": "https://discordapp.com/api/oauth2/token", - "AuthURL": "https://discordapp.com/api/oauth2/authorize" - }, - "Frontend": { - "URL": "http://localhost:8043/" - }, - "EMailClientSettings": { - "Host": "mail.sh-edraft.de", - "Port": "587", - "UserName": "dev-srv@sh-edraft.de", - "Credentials": "RmBOQX1eNFYiYjgsSid3fV1nelc2WA==" - } -} \ No newline at end of file diff --git a/kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json b/kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json deleted file mode 100644 index f122db51..00000000 --- a/kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "Api": { - "Port": 5000, - "Host": "0.0.0.0", - "RedirectToHTTPS": false - }, - "Authentication": { - "SecretKey": "RjNiNUxEeisjSnZ6Zz1XIUBnc2EleHNG", - "Issuer": "http://localhost:5000", - "Audience": "http://localhost:4200", - "TokenExpireTime": 1, - "RefreshTokenExpireTime": 7 - }, - "DiscordAuthentication": { - "ClientSecret": "V3FTb3JYVFBiVktEeHZxdWJDWW4xcnBCbXRwdmpwcy0=", - "_RedirectURL": "http://localhost:5000/api/auth/discord/register", - "RedirectURL": "http://localhost:4200/auth/register", - "Scope": [ - "identify", - "email" - ], - "TokenURL": "https://discordapp.com/api/oauth2/token", - "AuthURL": "https://discordapp.com/api/oauth2/authorize" - }, - "Frontend": { - "URL": "http://localhost:4200/" - } -} \ No newline at end of file diff --git a/kdb-bot/src/bot_api/config/apisettings.edrafts-pc.json b/kdb-bot/src/bot_api/config/apisettings.edrafts-pc.json deleted file mode 100644 index 100812aa..00000000 --- a/kdb-bot/src/bot_api/config/apisettings.edrafts-pc.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "Api": { - "Port": 8044, - "Host": "0.0.0.0", - "RedirectToHTTPS": false - }, - "Authentication": { - "SecretKey": "RjNiNUxEeisjSnZ6Zz1XIUBnc2EleHNG", - "Issuer": "http://localhost:8084", - "Audience": "http://localhost:4200", - "TokenExpireTime": 1, - "RefreshTokenExpireTime": 7 - }, - "DiscordAuthentication": { - "ClientSecret": "V3FTb3JYVFBiVktEeHZxdWJDWW4xcnBCbXRwdmpwcy0=", - "RedirectURL": "http://localhost:4200/auth/register", - "Scope": [ - "identify", - "email" - ], - "TokenURL": "https://discordapp.com/api/oauth2/token", - "AuthURL": "https://discordapp.com/api/oauth2/authorize" - }, - "Frontend": { - "URL": "http://localhost:4200/" - } -} \ No newline at end of file diff --git a/kdb-bot/src/bot_api/config/apisettings.json b/kdb-bot/src/bot_api/config/apisettings.json deleted file mode 100644 index 9e26dfee..00000000 --- a/kdb-bot/src/bot_api/config/apisettings.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/kdb-bot/src/bot_api/config/apisettings.production.json b/kdb-bot/src/bot_api/config/apisettings.production.json deleted file mode 100644 index 5eb0107f..00000000 --- a/kdb-bot/src/bot_api/config/apisettings.production.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "Api": { - "Port": 80, - "Host": "0.0.0.0", - "RedirectToHTTPS": false - }, - "Authentication": { - "SecretKey": "cEwzW2BdcWxGLTBdPClJImNIbDFJXjVsPGw=", - "Issuer": "https://kdb.keksdose-gaming.de/", - "Audience": "https://kdb.keksdose-gaming.de/", - "TokenExpireTime": 1, - "RefreshTokenExpireTime": 7 - }, - "DiscordAuthentication": { - "ClientSecret": "SXpIOGY4ZzhWVEljOXJwSk5QcVpNU0lmRDNTb2c1Vk8=", - "RedirectURL": "https://kdb.keksdose-gaming.de/auth/register", - "Scope": [ - "identify", - "email" - ], - "TokenURL": "https://discordapp.com/api/oauth2/token", - "AuthURL": "https://discordapp.com/api/oauth2/authorize" - }, - "Frontend": { - "URL": "https://kdb.keksdose-gaming.de/" - }, - "EMailClientSettings": { - "Host": "mail.sh-edraft.de", - "Port": "587", - "UserName": "kruemmelmonster@sh-edraft.de", - "Credentials": "YjAwT3tPSVspezdadExdOEkoV3M3XiNb" - } -} \ No newline at end of file diff --git a/kdb-bot/src/bot_api/config/apisettings.staging.json b/kdb-bot/src/bot_api/config/apisettings.staging.json deleted file mode 100644 index 5ce66fc3..00000000 --- a/kdb-bot/src/bot_api/config/apisettings.staging.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "Api": { - "Port": 80, - "Host": "0.0.0.0", - "RedirectToHTTPS": false - }, - "Authentication": { - "SecretKey": "Kj87RjklLUM1MytsUjtbcCswRidBV2VdMXU=", - "Issuer": "https://kdb-test.keksdose-gaming.de/", - "Audience": "https://kdb-test.keksdose-gaming.de/", - "TokenExpireTime": 1, - "RefreshTokenExpireTime": 7 - }, - "DiscordAuthentication": { - "ClientSecret": "VVdRZTg1SnFxUExCNmhzU1RZY05mTHV5TmVaV0NkUmc=", - "RedirectURL": "https://kdb-test.keksdose-gaming.de/auth/register", - "Scope": [ - "identify", - "email" - ], - "TokenURL": "https://discordapp.com/api/oauth2/token", - "AuthURL": "https://discordapp.com/api/oauth2/authorize" - }, - "Frontend": { - "URL": "https://kdb-test.keksdose-gaming.de/" - }, - "EMailClientSettings": { - "Host": "mail.sh-edraft.de", - "Port": "587", - "UserName": "kruemmelmonster@sh-edraft.de", - "Credentials": "YjAwT3tPSVspezdadExdOEkoV3M3XiNb" - } -} \ No newline at end of file diff --git a/kdb-bot/src/bot_api/config/appsettings.PC-Nick.json b/kdb-bot/src/bot_api/config/appsettings.PC-Nick.json deleted file mode 100644 index 9e26dfee..00000000 --- a/kdb-bot/src/bot_api/config/appsettings.PC-Nick.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file From 7bb407f2d736c6ce50263a5e5a8015abfde84f11 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 10 Nov 2022 07:47:43 +0100 Subject: [PATCH 169/275] Added configs --- kdb-bot/src/bot/config | 2 +- kdb-bot/src/bot_api/config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index fb3afa7e..5e5ae0d6 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit fb3afa7e74da374270843eae1850ff6b60819600 +Subproject commit 5e5ae0d6f0e48829094bd371fb4e2ded74c9bfc9 diff --git a/kdb-bot/src/bot_api/config b/kdb-bot/src/bot_api/config index 90197e75..dc5c45a5 160000 --- a/kdb-bot/src/bot_api/config +++ b/kdb-bot/src/bot_api/config @@ -1 +1 @@ -Subproject commit 90197e75e535a74caee41b4f39fd459ffb1891b5 +Subproject commit dc5c45a5c6533d39bd88e9920c751cc9f605b968 From b0b228eef673c0c9f52d6ea97eecbc4348bf206b Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 10 Nov 2022 07:54:20 +0100 Subject: [PATCH 170/275] Moved docker related files --- .gitmodules | 3 ++ kdb-bot/docker-compose.dev.yml | 49 ------------------------------ kdb-bot/docker-compose.staging.yml | 49 ------------------------------ kdb-bot/docker-compose.yml | 48 ----------------------------- kdb-bot/dockerfile | 18 ----------- 5 files changed, 3 insertions(+), 164 deletions(-) delete mode 100644 kdb-bot/docker-compose.dev.yml delete mode 100644 kdb-bot/docker-compose.staging.yml delete mode 100644 kdb-bot/docker-compose.yml delete mode 100644 kdb-bot/dockerfile diff --git a/.gitmodules b/.gitmodules index 1b416f60..ca793c6c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "kdb-bot/src/bot_api/config"] path = kdb-bot/src/bot_api/config url = https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot.api.config.git +[submodule "kdb-bot/docker"] + path = kdb-bot/docker + url = https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot.docker.git diff --git a/kdb-bot/docker-compose.dev.yml b/kdb-bot/docker-compose.dev.yml deleted file mode 100644 index e73d2d93..00000000 --- a/kdb-bot/docker-compose.dev.yml +++ /dev/null @@ -1,49 +0,0 @@ -version: "3.9" - -volumes: - kdb_bot_dev_1: - kdb_web_dev_1: - kdb_db_dev_1: - -services: - kdb_bot_dev_1: - image: kdb-bot/kdb-bot:0.3 - container_name: kdb_bot_dev_1 - depends_on: - - kdb_db_dev_1 - volumes: - - kdb_bot_dev_1:/app - environment: - KDB_ENVIRONMENT: "dev" - KDB_TOKEN: "OTk4MTU5NjczODkzMDYwNzM4.GN3QyA.yvWO6L7Eu36gXQ7ARDs0Jg2J1VqIDnHLou5lT4" - KDB_PREFIX: "!kd " - restart: 'no' - ports: - - '8044:80' - command: bash /app/bot/bot -dev - - kdb_web_dev_1: - image: kdb-web/kdb-web:0.3 - container_name: kdb_web_dev_1 - depends_on: - - kdb_bot_dev_1 - volumes: - - kdb_web_dev_1:/app - restart: 'no' - ports: - - '8043:80' - - kdb_db_dev_1: - image: mysql:latest - container_name: kdb_db_dev_1 - command: mysqld --default-authentication-plugin=mysql_native_password - restart: unless-stopped - environment: - MYSQL_ROOT_PASSWORD: "kd_kdb" - MYSQL_USER: "kd_kdb" - MYSQL_PASSWORD: "!w5D_&steCejfjq~b{0_DP@e:§X2?JUz" - MYSQL_DATABASE: "kd_kdb" - ports: - - "3308:3306" - volumes: - - kdb_db_dev_1:/var/lib/mysql \ No newline at end of file diff --git a/kdb-bot/docker-compose.staging.yml b/kdb-bot/docker-compose.staging.yml deleted file mode 100644 index 129ebb96..00000000 --- a/kdb-bot/docker-compose.staging.yml +++ /dev/null @@ -1,49 +0,0 @@ -version: "3.9" - -volumes: - kdb_bot_staging_1: - kdb_web_staging_1: - kdb_db_staging_1: - -services: - kdb_bot_staging_1: - image: kdb-bot/kdb-bot:0.3 - container_name: kdb_bot_staging_1 - depends_on: - - kdb_db_staging_1 - volumes: - - kdb_bot_staging_1:/app - environment: - KDB_ENVIRONMENT: "staging" - KDB_TOKEN: "OTk4MTU5ODAyMzkzOTY0NTk0.G4rLkF.uBQ9pW8X1Lm5agHqvBfzf7qEf8Ton-3a1oJPmY" - KDB_PREFIX: "!kt " - restart: 'no' - ports: - - '8044:80' - command: bash /app/bot/bot -stage - - kdb_web_staging_1: - image: kdb-web/kdb-web:0.3 - container_name: kdb_web_staging_1 - depends_on: - - kdb_bot_staging_1 - volumes: - - kdb_web_staging_1:/app - restart: 'no' - ports: - - '8043:80' - - kdb_db_staging_1: - image: mysql:latest - container_name: kdb_db_staging_1 - command: mysqld --default-authentication-plugin=mysql_native_password - restart: unless-stopped - environment: - MYSQL_ROOT_PASSWORD: "kd_kdb" - MYSQL_USER: "kd_kdb" - MYSQL_PASSWORD: "3&YwVMCwüb=LUt7B§ÖsY?Kr~XRtD#&&f" - MYSQL_DATABASE: "kd_kdb" - ports: - - "3308:3306" - volumes: - - kdb_db_staging_1:/var/lib/mysql \ No newline at end of file diff --git a/kdb-bot/docker-compose.yml b/kdb-bot/docker-compose.yml deleted file mode 100644 index fd3e7db2..00000000 --- a/kdb-bot/docker-compose.yml +++ /dev/null @@ -1,48 +0,0 @@ -version: "3.9" - -volumes: - kdb_bot_prod_1: - kdb_web_prod_1: - kdb_db_prod_1: - -services: - kdb_bot_prod_1: - image: kdb-bot/kdb-bot:0.3 - container_name: kdb_bot_prod_1 - depends_on: - - kdb_db_prod_1 - volumes: - - kdb_bot_prod_1:/app - environment: - KDB_ENVIRONMENT: "production" - KDB_TOKEN: "" - KDB_PREFIX: "!k " - restart: 'no' - ports: - - '8041:80' - - kdb_web_prod_1: - image: kdb-web/kdb-web:0.3 - container_name: kdb_web_prod_1 - depends_on: - - kdb_bot_prod_1 - volumes: - - kdb_web_prod_1:/app - restart: 'no' - ports: - - '8042:80' - - kdb_db_prod_1: - image: mysql:latest - container_name: kdb_db_prod_1 - command: mysqld --default-authentication-plugin=mysql_native_password - restart: unless-stopped - environment: - MYSQL_ROOT_PASSWORD: "kd_kdb" - MYSQL_USER: "kd_kdb" - MYSQL_PASSWORD: "+=gj}(ÄEbRG6_S&ö}ü>zaNT=rE{_~m Date: Thu, 10 Nov 2022 07:54:20 +0100 Subject: [PATCH 171/275] Moved docker related files --- .gitmodules | 3 ++ kdb-bot/docker | 1 + kdb-bot/docker-compose.dev.yml | 49 ------------------------------ kdb-bot/docker-compose.staging.yml | 49 ------------------------------ kdb-bot/docker-compose.yml | 48 ----------------------------- kdb-bot/dockerfile | 18 ----------- 6 files changed, 4 insertions(+), 164 deletions(-) create mode 160000 kdb-bot/docker delete mode 100644 kdb-bot/docker-compose.dev.yml delete mode 100644 kdb-bot/docker-compose.staging.yml delete mode 100644 kdb-bot/docker-compose.yml delete mode 100644 kdb-bot/dockerfile diff --git a/.gitmodules b/.gitmodules index 1b416f60..ca793c6c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "kdb-bot/src/bot_api/config"] path = kdb-bot/src/bot_api/config url = https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot.api.config.git +[submodule "kdb-bot/docker"] + path = kdb-bot/docker + url = https://git.sh-edraft.de/sh-edraft.de/kd_discord_bot.docker.git diff --git a/kdb-bot/docker b/kdb-bot/docker new file mode 160000 index 00000000..73ccace0 --- /dev/null +++ b/kdb-bot/docker @@ -0,0 +1 @@ +Subproject commit 73ccace0b83ee29f73a91d1e25d7d42f83479c6a diff --git a/kdb-bot/docker-compose.dev.yml b/kdb-bot/docker-compose.dev.yml deleted file mode 100644 index e73d2d93..00000000 --- a/kdb-bot/docker-compose.dev.yml +++ /dev/null @@ -1,49 +0,0 @@ -version: "3.9" - -volumes: - kdb_bot_dev_1: - kdb_web_dev_1: - kdb_db_dev_1: - -services: - kdb_bot_dev_1: - image: kdb-bot/kdb-bot:0.3 - container_name: kdb_bot_dev_1 - depends_on: - - kdb_db_dev_1 - volumes: - - kdb_bot_dev_1:/app - environment: - KDB_ENVIRONMENT: "dev" - KDB_TOKEN: "OTk4MTU5NjczODkzMDYwNzM4.GN3QyA.yvWO6L7Eu36gXQ7ARDs0Jg2J1VqIDnHLou5lT4" - KDB_PREFIX: "!kd " - restart: 'no' - ports: - - '8044:80' - command: bash /app/bot/bot -dev - - kdb_web_dev_1: - image: kdb-web/kdb-web:0.3 - container_name: kdb_web_dev_1 - depends_on: - - kdb_bot_dev_1 - volumes: - - kdb_web_dev_1:/app - restart: 'no' - ports: - - '8043:80' - - kdb_db_dev_1: - image: mysql:latest - container_name: kdb_db_dev_1 - command: mysqld --default-authentication-plugin=mysql_native_password - restart: unless-stopped - environment: - MYSQL_ROOT_PASSWORD: "kd_kdb" - MYSQL_USER: "kd_kdb" - MYSQL_PASSWORD: "!w5D_&steCejfjq~b{0_DP@e:§X2?JUz" - MYSQL_DATABASE: "kd_kdb" - ports: - - "3308:3306" - volumes: - - kdb_db_dev_1:/var/lib/mysql \ No newline at end of file diff --git a/kdb-bot/docker-compose.staging.yml b/kdb-bot/docker-compose.staging.yml deleted file mode 100644 index 129ebb96..00000000 --- a/kdb-bot/docker-compose.staging.yml +++ /dev/null @@ -1,49 +0,0 @@ -version: "3.9" - -volumes: - kdb_bot_staging_1: - kdb_web_staging_1: - kdb_db_staging_1: - -services: - kdb_bot_staging_1: - image: kdb-bot/kdb-bot:0.3 - container_name: kdb_bot_staging_1 - depends_on: - - kdb_db_staging_1 - volumes: - - kdb_bot_staging_1:/app - environment: - KDB_ENVIRONMENT: "staging" - KDB_TOKEN: "OTk4MTU5ODAyMzkzOTY0NTk0.G4rLkF.uBQ9pW8X1Lm5agHqvBfzf7qEf8Ton-3a1oJPmY" - KDB_PREFIX: "!kt " - restart: 'no' - ports: - - '8044:80' - command: bash /app/bot/bot -stage - - kdb_web_staging_1: - image: kdb-web/kdb-web:0.3 - container_name: kdb_web_staging_1 - depends_on: - - kdb_bot_staging_1 - volumes: - - kdb_web_staging_1:/app - restart: 'no' - ports: - - '8043:80' - - kdb_db_staging_1: - image: mysql:latest - container_name: kdb_db_staging_1 - command: mysqld --default-authentication-plugin=mysql_native_password - restart: unless-stopped - environment: - MYSQL_ROOT_PASSWORD: "kd_kdb" - MYSQL_USER: "kd_kdb" - MYSQL_PASSWORD: "3&YwVMCwüb=LUt7B§ÖsY?Kr~XRtD#&&f" - MYSQL_DATABASE: "kd_kdb" - ports: - - "3308:3306" - volumes: - - kdb_db_staging_1:/var/lib/mysql \ No newline at end of file diff --git a/kdb-bot/docker-compose.yml b/kdb-bot/docker-compose.yml deleted file mode 100644 index fd3e7db2..00000000 --- a/kdb-bot/docker-compose.yml +++ /dev/null @@ -1,48 +0,0 @@ -version: "3.9" - -volumes: - kdb_bot_prod_1: - kdb_web_prod_1: - kdb_db_prod_1: - -services: - kdb_bot_prod_1: - image: kdb-bot/kdb-bot:0.3 - container_name: kdb_bot_prod_1 - depends_on: - - kdb_db_prod_1 - volumes: - - kdb_bot_prod_1:/app - environment: - KDB_ENVIRONMENT: "production" - KDB_TOKEN: "" - KDB_PREFIX: "!k " - restart: 'no' - ports: - - '8041:80' - - kdb_web_prod_1: - image: kdb-web/kdb-web:0.3 - container_name: kdb_web_prod_1 - depends_on: - - kdb_bot_prod_1 - volumes: - - kdb_web_prod_1:/app - restart: 'no' - ports: - - '8042:80' - - kdb_db_prod_1: - image: mysql:latest - container_name: kdb_db_prod_1 - command: mysqld --default-authentication-plugin=mysql_native_password - restart: unless-stopped - environment: - MYSQL_ROOT_PASSWORD: "kd_kdb" - MYSQL_USER: "kd_kdb" - MYSQL_PASSWORD: "+=gj}(ÄEbRG6_S&ö}ü>zaNT=rE{_~m Date: Thu, 10 Nov 2022 08:03:12 +0100 Subject: [PATCH 172/275] Changed dev pws --- kdb-bot/docker | 2 +- kdb-bot/src/bot/config | 2 +- kdb-bot/src/bot_api/config | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kdb-bot/docker b/kdb-bot/docker index 73ccace0..cd15cf9f 160000 --- a/kdb-bot/docker +++ b/kdb-bot/docker @@ -1 +1 @@ -Subproject commit 73ccace0b83ee29f73a91d1e25d7d42f83479c6a +Subproject commit cd15cf9f4d58197f79a928a7ba134048c1253ba6 diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 5e5ae0d6..414083f9 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 5e5ae0d6f0e48829094bd371fb4e2ded74c9bfc9 +Subproject commit 414083f9c09b700b10de2008a623d79a70efcd2b diff --git a/kdb-bot/src/bot_api/config b/kdb-bot/src/bot_api/config index dc5c45a5..8cbb363d 160000 --- a/kdb-bot/src/bot_api/config +++ b/kdb-bot/src/bot_api/config @@ -1 +1 @@ -Subproject commit dc5c45a5c6533d39bd88e9920c751cc9f605b968 +Subproject commit 8cbb363d142e3b8fd8e649f9ee97139ff2ac3137 From 97fab6adca9137866c1c2aabe8ed93f025828ab3 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 10 Nov 2022 08:12:43 +0100 Subject: [PATCH 173/275] Changed stage pws --- kdb-bot/docker | 2 +- kdb-bot/src/bot/config | 2 +- kdb-bot/src/bot_api/config | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kdb-bot/docker b/kdb-bot/docker index cd15cf9f..816ac5c0 160000 --- a/kdb-bot/docker +++ b/kdb-bot/docker @@ -1 +1 @@ -Subproject commit cd15cf9f4d58197f79a928a7ba134048c1253ba6 +Subproject commit 816ac5c0004b4bea46f38f40f2eda07112117c32 diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 414083f9..5288fdc9 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 414083f9c09b700b10de2008a623d79a70efcd2b +Subproject commit 5288fdc9c191d3d94c1bee0035147f957e1122ea diff --git a/kdb-bot/src/bot_api/config b/kdb-bot/src/bot_api/config index 8cbb363d..2f7462be 160000 --- a/kdb-bot/src/bot_api/config +++ b/kdb-bot/src/bot_api/config @@ -1 +1 @@ -Subproject commit 8cbb363d142e3b8fd8e649f9ee97139ff2ac3137 +Subproject commit 2f7462be54fadca881d2e32bd5a20e15669fd802 From 26f09385665b8c2b3f8e647eb0d99432fe501e71 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 10 Nov 2022 08:15:50 +0100 Subject: [PATCH 174/275] Changed prod pws --- kdb-bot/docker | 2 +- kdb-bot/src/bot/config | 2 +- kdb-bot/src/bot_api/config | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kdb-bot/docker b/kdb-bot/docker index 816ac5c0..b3d5e9fe 160000 --- a/kdb-bot/docker +++ b/kdb-bot/docker @@ -1 +1 @@ -Subproject commit 816ac5c0004b4bea46f38f40f2eda07112117c32 +Subproject commit b3d5e9feef881960c2e65e6661b36123b4d6fce5 diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 5288fdc9..90e446c9 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 5288fdc9c191d3d94c1bee0035147f957e1122ea +Subproject commit 90e446c9061c3e13bd943fe1829b0a784f45f001 diff --git a/kdb-bot/src/bot_api/config b/kdb-bot/src/bot_api/config index 2f7462be..1aede944 160000 --- a/kdb-bot/src/bot_api/config +++ b/kdb-bot/src/bot_api/config @@ -1 +1 @@ -Subproject commit 2f7462be54fadca881d2e32bd5a20e15669fd802 +Subproject commit 1aede94493e3385e184ba25a54d9b0225634988e From 766e9b1235061d5219d100e1569437e260c00300 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 10 Nov 2022 08:17:44 +0100 Subject: [PATCH 175/275] Changed edrafts secrets --- kdb-bot/src/bot/config | 2 +- kdb-bot/src/bot_api/config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 90e446c9..18369b32 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 90e446c9061c3e13bd943fe1829b0a784f45f001 +Subproject commit 18369b3283fa5bdb6958054aeb126f6b376a09b6 diff --git a/kdb-bot/src/bot_api/config b/kdb-bot/src/bot_api/config index 1aede944..eef10d58 160000 --- a/kdb-bot/src/bot_api/config +++ b/kdb-bot/src/bot_api/config @@ -1 +1 @@ -Subproject commit 1aede94493e3385e184ba25a54d9b0225634988e +Subproject commit eef10d58253f80bd8a4c18f3d10e0d56408084d4 From b30d49f5d73fd5700d147442b22df727e525fdaf Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 10 Nov 2022 08:20:49 +0100 Subject: [PATCH 176/275] Updated my laptop config --- kdb-bot/src/bot/config | 2 +- kdb-bot/src/bot_api/config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 18369b32..29880951 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 18369b3283fa5bdb6958054aeb126f6b376a09b6 +Subproject commit 29880951926763f6838c4458c1233a5738f08ae2 diff --git a/kdb-bot/src/bot_api/config b/kdb-bot/src/bot_api/config index eef10d58..4060fbf3 160000 --- a/kdb-bot/src/bot_api/config +++ b/kdb-bot/src/bot_api/config @@ -1 +1 @@ -Subproject commit eef10d58253f80bd8a4c18f3d10e0d56408084d4 +Subproject commit 4060fbf39c204d498150f8988f1f566cb812788f From 2a4933f9718c94a720c88f6d99fa170884083902 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 9 Nov 2022 22:34:25 +0100 Subject: [PATCH 177/275] Added ontime to user info #97 --- kdb-bot/src/bot/translation/de.json | 1 + kdb-bot/src/bot_data/abc/user_joined_voice_channel_abc.py | 2 +- kdb-bot/src/modules/base/command/user_group.py | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 606e89c6..fc7e5631 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -162,6 +162,7 @@ "discord_join": "Discord beigetreten am", "last_join": "Server beigetreten am", "xp": "XP", + "ontime": "Ontime", "roles": "Rollen", "joins": "Beitritte", "lefts": "Abgänge", diff --git a/kdb-bot/src/bot_data/abc/user_joined_voice_channel_abc.py b/kdb-bot/src/bot_data/abc/user_joined_voice_channel_abc.py index a9228c48..d46a6f2b 100644 --- a/kdb-bot/src/bot_data/abc/user_joined_voice_channel_abc.py +++ b/kdb-bot/src/bot_data/abc/user_joined_voice_channel_abc.py @@ -16,7 +16,7 @@ class UserJoinedVoiceChannelRepositoryABC(ABC): def get_user_joined_voice_channel_by_id(self, id: int) -> UserJoinedVoiceChannel: pass @abstractmethod - def get_user_joined_voice_channels_by_user_id(self, user_id: int) -> list[UserJoinedVoiceChannel]: pass + def get_user_joined_voice_channels_by_user_id(self, user_id: int) -> List[UserJoinedVoiceChannel]: pass @abstractmethod def get_active_user_joined_voice_channel_by_user_id(self, user_id: int) -> UserJoinedVoiceChannel: pass diff --git a/kdb-bot/src/modules/base/command/user_group.py b/kdb-bot/src/modules/base/command/user_group.py index 1988902b..a2efb8ba 100644 --- a/kdb-bot/src/modules/base/command/user_group.py +++ b/kdb-bot/src/modules/base/command/user_group.py @@ -14,6 +14,7 @@ from bot_core.logging.command_logger import CommandLogger from bot_core.pipes.date_time_offset_pipe import DateTimeOffsetPipe from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC +from bot_data.abc.user_joined_voice_channel_abc import UserJoinedVoiceChannelRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -31,6 +32,7 @@ class UserGroup(DiscordCommandABC): servers: ServerRepositoryABC, users: UserRepositoryABC, user_joined_servers: UserJoinedServerRepositoryABC, + user_joined_voice_channel: UserJoinedVoiceChannelRepositoryABC, translate: TranslatePipe, date: DateTimeOffsetPipe ): @@ -45,6 +47,7 @@ class UserGroup(DiscordCommandABC): self._servers = servers self._users = users self._user_joined_servers = user_joined_servers + self._user_joined_voice_channel = user_joined_voice_channel self._t = translate self._date = date @@ -81,11 +84,15 @@ class UserGroup(DiscordCommandABC): color=int('ef9d0d', 16) ) + ujvs = self._user_joined_voice_channel.get_user_joined_voice_channels_by_user_id(user.user_id) + ontime = ujvs.sum(lambda join: round((join.leaved_on - join.joined_on).total_seconds() / 3600, 2)) + embed.add_field(name=self._t.transform('modules.base.user_info.fields.id'), value=member.id) embed.add_field(name=self._t.transform('modules.base.user_info.fields.name'), value=member.name) embed.add_field(name=self._t.transform('modules.base.user_info.fields.discord_join'), value=self._date.transform(member.created_at), inline=False) embed.add_field(name=self._t.transform('modules.base.user_info.fields.last_join'), value=self._date.transform(member.joined_at), inline=False) embed.add_field(name=self._t.transform('modules.base.user_info.fields.xp'), value=str(user.xp)) + embed.add_field(name=self._t.transform('modules.base.user_info.fields.ontime'), value=str(ontime)) roles = '' for role in member.roles: From 44f22e6aee9c5ff2d6cf887fe578ed6dfbe97203 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 10 Nov 2022 16:33:04 +0100 Subject: [PATCH 178/275] Added level edit command #103 --- kdb-bot/src/bot/translation/de.json | 6 +- .../src/modules/level/command/level_group.py | 116 ++++++++++++++---- 2 files changed, 96 insertions(+), 26 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 606e89c6..7b87852d 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -191,7 +191,11 @@ "permission_int": "Berechtigungen" }, "create": { - "created": "Level {} mit Berechtigungen {} wurde erstellt." + "created": "Level {} mit Berechtigungen {} wurde erstellt :D" + }, + "edit": { + "edited": "Level {} wurde bearbeitet :D", + "not_found": "Level {} nicht gefunden!" }, "remove": { "success": "Level {} wurde entfernt :D", diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 7a49593c..00e4bd6f 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -55,6 +55,31 @@ class LevelGroup(DiscordCommandABC): self._level_service = level_service self._level_seeder = level_seeder + self._colors = [ + ('blue', discord.Colour.blue().to_rgb()), + ('dark_blue', discord.Colour.dark_blue().to_rgb()), + ('dark_gold', discord.Colour.dark_gold().to_rgb()), + ('dark_gray', discord.Colour.dark_gray().to_rgb()), + ('dark_green', discord.Colour.dark_green().to_rgb()), + ('dark_grey', discord.Colour.dark_grey().to_rgb()), + ('dark_magenta', discord.Colour.dark_magenta().to_rgb()), + ('dark_orange', discord.Colour.dark_orange().to_rgb()), + ('dark_purple', discord.Colour.dark_purple().to_rgb()), + ('dark_red', discord.Colour.dark_red().to_rgb()), + ('dark_teal', discord.Colour.dark_teal().to_rgb()), + ('default', discord.Colour.default().to_rgb()), + ('gold', discord.Colour.gold().to_rgb()), + ('green', discord.Colour.green().to_rgb()), + ('greyple', discord.Colour.greyple().to_rgb()), + ('light_grey', discord.Colour.light_grey().to_rgb()), + ('magenta', discord.Colour.magenta().to_rgb()), + ('orange', discord.Colour.orange().to_rgb()), + ('purple', discord.Colour.purple().to_rgb()), + ('red', discord.Colour.red().to_rgb()), + ('teal', discord.Colour.teal().to_rgb()), + ('yellow', discord.Colour.yellow().to_rgb()) + ] + self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') async def _seed_levels(self, channel: discord.TextChannel): @@ -163,33 +188,74 @@ class LevelGroup(DiscordCommandABC): @create.autocomplete('color') async def create_color_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: - colors = [ - ('blue', discord.Colour.blue().to_rgb()), - ('dark_blue', discord.Colour.dark_blue().to_rgb()), - ('dark_gold', discord.Colour.dark_gold().to_rgb()), - ('dark_gray', discord.Colour.dark_gray().to_rgb()), - ('dark_green', discord.Colour.dark_green().to_rgb()), - ('dark_grey', discord.Colour.dark_grey().to_rgb()), - ('dark_magenta', discord.Colour.dark_magenta().to_rgb()), - ('dark_orange', discord.Colour.dark_orange().to_rgb()), - ('dark_purple', discord.Colour.dark_purple().to_rgb()), - ('dark_red', discord.Colour.dark_red().to_rgb()), - ('dark_teal', discord.Colour.dark_teal().to_rgb()), - ('default', discord.Colour.default().to_rgb()), - ('gold', discord.Colour.gold().to_rgb()), - ('green', discord.Colour.green().to_rgb()), - ('greyple', discord.Colour.greyple().to_rgb()), - ('light_grey', discord.Colour.light_grey().to_rgb()), - ('magenta', discord.Colour.magenta().to_rgb()), - ('orange', discord.Colour.orange().to_rgb()), - ('purple', discord.Colour.purple().to_rgb()), - ('red', discord.Colour.red().to_rgb()), - ('teal', discord.Colour.teal().to_rgb()), - ('yellow', discord.Colour.yellow().to_rgb()) - ] # value in rg format see: # https://discordpy.readthedocs.io/en/latest/api.html#discord.Colour.to_rgb - return [app_commands.Choice(name=self._t.transform(f'common.colors.{color}'), value=f'rgb({code[0]}, {code[1]}, {code[2]})') for color, code in colors] + return [app_commands.Choice(name=self._t.transform(f'common.colors.{color}'), value=f'rgb({code[0]}, {code[1]}, {code[2]})') for color, code in self._colors] + + @level.command() + @commands.guild_only() + async def edit(self, ctx: Context, level: str, name: str = None, color: str = None, min_xp: int = None, permissions: int = None): + self._logger.debug(__name__, f'Received command level edit {ctx}') + if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): + return + self._client_utils.received_command(ctx.guild.id) + + if ctx.guild is None: + return + + server = self._servers.get_server_by_discord_id(ctx.guild.id) + level_from_db = self._levels.get_levels_by_server_id(server.server_id).where(lambda l: l.name == level).single_or_default() + if level_from_db is None: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.edit.not_found').format(level)) + return + + guild: Guild = self._bot.guilds.where(lambda g: g == ctx.guild).single() + role: Role = guild.roles.where(lambda r: r.name == level_from_db.name).single() + + if name is not None: + level_from_db.name = name + + if color is not None: + try: + level_from_db.color = hex(discord.Colour.from_str(color).value) + except Exception as e: + self._logger.error(__name__, f'Error parsing color {color}', e) + return + + if min_xp is not None: + level_from_db.min_xp = min_xp + + if permissions is not None: + try: + level_from_db.permissions = discord.Permissions(permissions).value + except Exception as e: + self._logger.error(__name__, f'Error parsing permissions {permissions}', e) + return + + try: + self._levels.update_level(level_from_db) + self._db.save_changes() + await role.edit(name=level_from_db.name, permissions=discord.Permissions(level_from_db.permissions), colour=discord.Colour(int(level_from_db.color, 16))) + self._logger.info(__name__, f'Saved level {level_from_db.name} with color {level_from_db.color}, min_xp {level_from_db.min_xp} and permissions {level_from_db.permissions}') + except Exception as e: + self._logger.error(__name__, f'Could not save level {level} with color {color}, min_xp {min_xp} and permissions {permissions}', e) + else: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.edit.edited').format(level)) + await self._seed_levels(ctx.channel) + + self._logger.trace(__name__, f'Finished command level edit') + + @edit.autocomplete('level') + async def edit_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + server = self._servers.get_server_by_discord_id(interaction.guild.id) + levels = self._levels.get_levels_by_server_id(server.server_id).select(lambda l: l.name) + return [app_commands.Choice(name=level, value=level) for level in levels] + + @edit.autocomplete('color') + async def edit_color_autocomplete(self, interaction: discord.Interaction, current: str) -> TList[app_commands.Choice[str]]: + # value in rg format see: + # https://discordpy.readthedocs.io/en/latest/api.html#discord.Colour.to_rgb + return [app_commands.Choice(name=self._t.transform(f'common.colors.{color}'), value=f'rgb({code[0]}, {code[1]}, {code[2]})') for color, code in self._colors] @level.command() @commands.guild_only() From ef41ce03d346e753dc2a07a1bc554fb67f9f2558 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 10 Nov 2022 19:50:42 +0100 Subject: [PATCH 179/275] Added functionality to auto-role rule add to add reaction to message #63 --- .../auto_role/command/auto_role_group.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/kdb-bot/src/modules/auto_role/command/auto_role_group.py b/kdb-bot/src/modules/auto_role/command/auto_role_group.py index 072a3bf5..6dbf51c8 100644 --- a/kdb-bot/src/modules/auto_role/command/auto_role_group.py +++ b/kdb-bot/src/modules/auto_role/command/auto_role_group.py @@ -7,7 +7,7 @@ from cpl_discord.container import TextChannel from cpl_discord.service import DiscordBotServiceABC from cpl_query.extension import List from cpl_translation import TranslatePipe -from discord import app_commands +from discord import app_commands, Guild from discord.ext import commands from discord.ext.commands import Context @@ -258,13 +258,27 @@ class AutoRoleGroup(DiscordCommandABC): self._logger.trace(__name__, f'Finished command auto-role rule add') return - if self._auto_roles.get_auto_role_rules_by_auto_role_id(auto_role).where(lambda r: r.emoji_name == emoji.name and int(role_id) == role.id).count() > 0: + if self._auto_roles.get_auto_role_rules_by_auto_role_id(auto_role).where(lambda r: r.emoji_name == emoji.name and int(role_id) == role.id).first_or_default() is not None: await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.add.error.already_exists').format(auto_role)) self._logger.trace(__name__, f'Finished command auto-role rule add') return self._auto_roles.add_auto_role_rule(AutoRoleRule(auto_role, emoji_name, int(role_id))) self._db_context.save_changes() + rule = self._auto_roles.get_auto_role_rules_by_auto_role_id(auto_role).where(lambda r: r.emoji_name == emoji.name and int(role_id) == role.id).single() + try: + message = await ctx.fetch_message(auto_role_from_db.discord_message_id) + guild: Guild = self._bot.guilds.where(lambda g: g == ctx.guild).single() + emoji = List(discord.Emoji, guild.emojis).where(lambda x: x.name == rule.emoji_name).single() + + if emoji is None: + self._logger.debug(__name__, f'Emoji {rule.emoji_name} not found') + return + await message.add_reaction(emoji) + self._logger.debug(__name__, f'Added reaction {rule.emoji_name} to message: {auto_role_from_db.discord_message_id}') + except Exception as e: + self._logger.error(__name__, f'Cannot add reaction {rule.emoji_name} to message: {auto_role_from_db.discord_message_id}', e) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.auto_role.rule.add.success').format(emoji, role.name, auto_role)) self._logger.trace(__name__, f'Finished command auto-role rule add') From 25cc98732d6a5d4dc444d02ec241187795789bc0 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 10 Nov 2022 20:02:53 +0100 Subject: [PATCH 180/275] Fixed restart after message #62 --- kdb-bot/src/modules/base/command/restart_command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/modules/base/command/restart_command.py b/kdb-bot/src/modules/base/command/restart_command.py index 56c48958..829810e0 100644 --- a/kdb-bot/src/modules/base/command/restart_command.py +++ b/kdb-bot/src/modules/base/command/restart_command.py @@ -58,8 +58,8 @@ class RestartCommand(DiscordCommandABC): self._config.add_configuration('IS_RESTART', 'true') await self._client_utils.presence_game('common.presence.restart') - await asyncio.sleep(self._settings.wait_for_restart) await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.admin.restart_message')) + await asyncio.sleep(self._settings.wait_for_restart) await self._bot.stop_async() self._logger.trace(__name__, f'Finished restart command') From 3c1f017e3aba49e4cb29c893189fe3c0b9ac5c2e Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 10 Nov 2022 20:26:29 +0100 Subject: [PATCH 181/275] Fixed admins cannot assign users to admins #88 --- kdb-bot/src/bot_api/json_processor.py | 4 ++++ kdb-bot/src/bot_api/model/auth_user_dto.py | 2 +- kdb-bot/src/bot_api/model/update_auth_user_dto.py | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/kdb-bot/src/bot_api/json_processor.py b/kdb-bot/src/bot_api/json_processor.py index a0ea0ac1..ede53d2c 100644 --- a/kdb-bot/src/bot_api/json_processor.py +++ b/kdb-bot/src/bot_api/json_processor.py @@ -1,3 +1,4 @@ +import enum from inspect import signature, Parameter from cpl_core.utils import String @@ -28,6 +29,9 @@ class JSONProcessor: if isinstance(value, dict): value = JSONProcessor.process(parameter.annotation, value) + if issubclass(parameter.annotation, enum.Enum): + value = parameter.annotation(value) + args.append(value) elif parameter.default != Parameter.empty: diff --git a/kdb-bot/src/bot_api/model/auth_user_dto.py b/kdb-bot/src/bot_api/model/auth_user_dto.py index 15efc559..347df8ad 100644 --- a/kdb-bot/src/bot_api/model/auth_user_dto.py +++ b/kdb-bot/src/bot_api/model/auth_user_dto.py @@ -85,7 +85,7 @@ class AuthUserDTO(DtoABC): self._email = values['email'] self._password = values['password'] self._is_confirmed = values['isConfirmed'] - self._auth_role = values['authRole'] + self._auth_role = AuthRoleEnum(values['authRole']) def to_dict(self) -> dict: return { diff --git a/kdb-bot/src/bot_api/model/update_auth_user_dto.py b/kdb-bot/src/bot_api/model/update_auth_user_dto.py index 254c0c72..ac71e033 100644 --- a/kdb-bot/src/bot_api/model/update_auth_user_dto.py +++ b/kdb-bot/src/bot_api/model/update_auth_user_dto.py @@ -33,8 +33,8 @@ class UpdateAuthUserDTO(DtoABC): return self._change_password def from_dict(self, values: dict): - self._auth_user = values['authUser'] - self._new_auth_user = values['newAuthUser'] + self._auth_user = AuthUserDTO().from_dict(values['authUser']) + self._new_auth_user = AuthUserDTO().from_dict(values['newAuthUser']) self._change_password = False if 'changePassword' not in values else bool(values['changePassword']) def to_dict(self) -> dict: From b4fce51b571f98d0d3c3249da776646c22c50496 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 10 Nov 2022 20:56:45 +0100 Subject: [PATCH 182/275] Added is admin check to level create --- kdb-bot/src/modules/level/command/level_group.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 7a49593c..e253846a 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -122,6 +122,11 @@ class LevelGroup(DiscordCommandABC): return self._client_utils.received_command(ctx.guild.id) + if not self._permissions.is_member_admin(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command level remove') + return + try: color = hex(discord.Colour.from_str(color).value) except Exception as e: From c70149e9c8efe2a464275f241ffc5c06969410e2 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 10 Nov 2022 21:10:48 +0100 Subject: [PATCH 183/275] Added is admin check to level edit #103 --- kdb-bot/src/modules/level/command/level_group.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 3a2c211b..27fff87a 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -205,6 +205,11 @@ class LevelGroup(DiscordCommandABC): return self._client_utils.received_command(ctx.guild.id) + if not self._permissions.is_member_admin(ctx.author): + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + self._logger.trace(__name__, f'Finished command level remove') + return + if ctx.guild is None: return From b2087042bc95f65253c5dd43794453164628255f Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 11 Nov 2022 07:33:24 +0100 Subject: [PATCH 184/275] Added error notification to level edit #103 --- kdb-bot/src/bot/translation/de.json | 2 ++ kdb-bot/src/modules/level/command/level_group.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index ba857845..b3bbf9eb 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -196,6 +196,8 @@ }, "edit": { "edited": "Level {} wurde bearbeitet :D", + "color_invalid": "Die Farbe {} ist ungültig!", + "permission_invalid": "Der Berechtigungswert {} ist ungültig!", "not_found": "Level {} nicht gefunden!" }, "remove": { diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index 27fff87a..c8253a2c 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -229,6 +229,7 @@ class LevelGroup(DiscordCommandABC): try: level_from_db.color = hex(discord.Colour.from_str(color).value) except Exception as e: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.edit.color_invalid').format(color)) self._logger.error(__name__, f'Error parsing color {color}', e) return @@ -239,6 +240,7 @@ class LevelGroup(DiscordCommandABC): try: level_from_db.permissions = discord.Permissions(permissions).value except Exception as e: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.level.edit.permission_invalid').format(permissions)) self._logger.error(__name__, f'Error parsing permissions {permissions}', e) return From d36a29042ec35ad14d78a247d307ab40b322bea6 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 12 Nov 2022 12:15:45 +0100 Subject: [PATCH 185/275] Fixed copy paste error in feature flags --- kdb-bot/src/bot/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 29880951..3ded15da 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 29880951926763f6838c4458c1233a5738f08ae2 +Subproject commit 3ded15dab47e532d4bbec568da736822b21005eb From e26944260370d4733ea207bb9a4506fe38abc234 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sat, 12 Nov 2022 14:29:50 +0100 Subject: [PATCH 186/275] Fixed bot project --- kdb-bot/src/bot/bot.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/kdb-bot/src/bot/bot.json b/kdb-bot/src/bot/bot.json index db8ef6fe..a830d029 100644 --- a/kdb-bot/src/bot/bot.json +++ b/kdb-bot/src/bot/bot.json @@ -54,15 +54,14 @@ "../bot_api/bot-api.json", "../bot_core/bot-core.json", "../bot_data/bot-data.json", - "../modules/base/base.json", - "../modules/admin/admin.json", "../modules/auto_role/auto-role.json", "../modules/base/base.json", "../modules/boot_log/boot-log.json", "../modules/database/database.json", "../modules/level/level.json", - "../modules/moderator/moderator.json", - "../modules/permission/permission.json" + "../modules/permission/level.json", + "../modules/permission/permission.json", + "../modules/permission/stats.json" ] } } \ No newline at end of file From 8fa6458d6ec1a5cc50e1c7d15c4f4d7450e0b512 Mon Sep 17 00:00:00 2001 From: theim Date: Sat, 12 Nov 2022 15:16:06 +0100 Subject: [PATCH 187/275] Added my configs --- kdb-bot/src/bot/config | 2 +- kdb-bot/src/bot_api/config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 3ded15da..6f63688f 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 3ded15dab47e532d4bbec568da736822b21005eb +Subproject commit 6f63688fd914ad6fb3fc5249e4b3763dc261997a diff --git a/kdb-bot/src/bot_api/config b/kdb-bot/src/bot_api/config index 4060fbf3..98303ffd 160000 --- a/kdb-bot/src/bot_api/config +++ b/kdb-bot/src/bot_api/config @@ -1 +1 @@ -Subproject commit 4060fbf39c204d498150f8988f1f566cb812788f +Subproject commit 98303ffd45445eecfad57f8b1be86729de3661d2 From e754a10241af2d7bf995e5936961c2fcaae66797 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 13 Nov 2022 11:56:42 +0100 Subject: [PATCH 188/275] Added command checks #114 --- kdb-bot/src/bot/main.py | 2 + .../bot_core/core_extension/core_extension.py | 28 +++++++ kdb-bot/src/bot_core/exception/__init__.py | 0 kdb-bot/src/bot_core/exception/check_error.py | 7 ++ kdb-bot/src/bot_core/helper/command_checks.py | 76 +++++++++++++++++++ .../modules/base/command/restart_command.py | 14 +--- .../events/base_on_command_error_event.py | 4 + 7 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 kdb-bot/src/bot_core/core_extension/core_extension.py create mode 100644 kdb-bot/src/bot_core/exception/__init__.py create mode 100644 kdb-bot/src/bot_core/exception/check_error.py create mode 100644 kdb-bot/src/bot_core/helper/command_checks.py diff --git a/kdb-bot/src/bot/main.py b/kdb-bot/src/bot/main.py index 9747634b..b5e2a6c2 100644 --- a/kdb-bot/src/bot/main.py +++ b/kdb-bot/src/bot/main.py @@ -12,6 +12,7 @@ from bot.startup_migration_extension import StartupMigrationExtension from bot.startup_module_extension import StartupModuleExtension from bot.startup_settings_extension import StartupSettingsExtension from bot_api.app_api_extension import AppApiExtension +from bot_core.core_extension.core_extension import CoreExtension from modules.boot_log.boot_log_extension import BootLogExtension from modules.database.database_extension import DatabaseExtension @@ -31,6 +32,7 @@ class Program: .use_extension(BootLogExtension) \ .use_extension(DatabaseExtension) \ .use_extension(AppApiExtension) \ + .use_extension(CoreExtension) \ .use_startup(Startup) self.app: Application = await app_builder.build_async() await self.app.run_async() diff --git a/kdb-bot/src/bot_core/core_extension/core_extension.py b/kdb-bot/src/bot_core/core_extension/core_extension.py new file mode 100644 index 00000000..fba66dd7 --- /dev/null +++ b/kdb-bot/src/bot_core/core_extension/core_extension.py @@ -0,0 +1,28 @@ +from cpl_core.application import ApplicationExtensionABC +from cpl_core.configuration import ConfigurationABC +from cpl_core.dependency_injection import ServiceProviderABC +from cpl_translation import TranslatePipe + +from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC +from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings +from bot_core.helper.command_checks import CommandChecks +from modules.permission.abc.permission_service_abc import PermissionServiceABC + + +class CoreExtension(ApplicationExtensionABC): + + def __init__(self): + ApplicationExtensionABC.__init__(self) + + async def run(self, config: ConfigurationABC, services: ServiceProviderABC): + feature_flags: FeatureFlagsSettings = config.get_configuration(FeatureFlagsSettings) + if not feature_flags.get_flag(FeatureFlagsEnum.core_module): + return + + permissions: PermissionServiceABC = services.get_service(PermissionServiceABC) + client_utils: ClientUtilsServiceABC = services.get_service(ClientUtilsServiceABC) + message_service: MessageServiceABC = services.get_service(MessageServiceABC) + t: TranslatePipe = services.get_service(TranslatePipe) + CommandChecks.init(permissions, client_utils, message_service, t) diff --git a/kdb-bot/src/bot_core/exception/__init__.py b/kdb-bot/src/bot_core/exception/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kdb-bot/src/bot_core/exception/check_error.py b/kdb-bot/src/bot_core/exception/check_error.py new file mode 100644 index 00000000..d5c9a2b8 --- /dev/null +++ b/kdb-bot/src/bot_core/exception/check_error.py @@ -0,0 +1,7 @@ +from discord.ext.commands import CommandError + + +class CheckError(CommandError): + + def __init__(self, message, *args): + CommandError.__init__(self, message, *args) diff --git a/kdb-bot/src/bot_core/helper/command_checks.py b/kdb-bot/src/bot_core/helper/command_checks.py new file mode 100644 index 00000000..6bb3fa30 --- /dev/null +++ b/kdb-bot/src/bot_core/helper/command_checks.py @@ -0,0 +1,76 @@ +from typing import Optional + +from cpl_translation import TranslatePipe +from discord.ext import commands +from discord.ext.commands import Context + +from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC +from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.exception.check_error import CheckError +from modules.permission.abc.permission_service_abc import PermissionServiceABC + + +class CommandChecks: + _permissions: Optional[PermissionServiceABC] = None + _client_utils: Optional[ClientUtilsServiceABC] = None + _message_service: Optional[MessageServiceABC] = None + _t: Optional[TranslatePipe] = None + + @classmethod + def init( + cls, + permissions: PermissionServiceABC, + client_utils: ClientUtilsServiceABC, + message_service: MessageServiceABC, + translate: TranslatePipe, + ): + cls._permissions = permissions + cls._client_utils = client_utils + cls._message_service = message_service + cls._t = translate + + @classmethod + def check_is_ready(cls): + async def check_if_bot_is_ready_yet_and_respond(ctx: Context) -> bool: + result = await cls._client_utils.check_if_bot_is_ready_yet_and_respond(ctx) + if not result: + raise CheckError(f'Bot is not ready') + return result + + return commands.check(check_if_bot_is_ready_yet_and_respond) + + @classmethod + def check_is_member_admin(cls): + async def check_is_member_admin(ctx: Context): + has_permission = cls._permissions.is_member_admin(ctx.author) + if not has_permission: + await cls._message_service.send_ctx_msg(ctx, cls._t.transform('common.no_permission_message')) + raise CheckError(f'Member {ctx.author.name} is not admin') + + return has_permission + + return commands.check(check_is_member_admin) + + @classmethod + def check_is_member_technician(cls): + async def check_is_member_technician(ctx: Context): + has_permission = cls._permissions.is_member_technician(ctx.author) + if not has_permission: + await cls._message_service.send_ctx_msg(ctx, cls._t.transform('common.no_permission_message')) + raise CheckError(f'Member {ctx.author.name} is not technician') + + return has_permission + + return commands.check(check_is_member_technician) + + @classmethod + def check_is_member_moderator(cls): + async def check_is_member_moderator(ctx: Context): + has_permission = cls._permissions.is_member_moderator(ctx.author) + if not has_permission: + await cls._message_service.send_ctx_msg(ctx, cls._t.transform('common.no_permission_message')) + raise CheckError(f'Member {ctx.author.name} is not moderator') + + return has_permission + + return commands.check(check_is_member_moderator) diff --git a/kdb-bot/src/modules/base/command/restart_command.py b/kdb-bot/src/modules/base/command/restart_command.py index 829810e0..c1fd3ae5 100644 --- a/kdb-bot/src/modules/base/command/restart_command.py +++ b/kdb-bot/src/modules/base/command/restart_command.py @@ -1,6 +1,5 @@ import asyncio -import discord from cpl_core.configuration import ConfigurationABC from cpl_discord.command import DiscordCommandABC from cpl_discord.service import DiscordBotServiceABC @@ -11,6 +10,7 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.configuration.bot_settings import BotSettings +from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -43,18 +43,10 @@ class RestartCommand(DiscordCommandABC): @commands.hybrid_command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def restart(self, ctx: Context): self._logger.debug(__name__, f'Received command restart {ctx}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished restart command') - return self._config.add_configuration('IS_RESTART', 'true') await self._client_utils.presence_game('common.presence.restart') diff --git a/kdb-bot/src/modules/base/events/base_on_command_error_event.py b/kdb-bot/src/modules/base/events/base_on_command_error_event.py index 72a7a20e..6233d978 100644 --- a/kdb-bot/src/modules/base/events/base_on_command_error_event.py +++ b/kdb-bot/src/modules/base/events/base_on_command_error_event.py @@ -13,6 +13,7 @@ from cpl_discord.events.on_command_error_abc import OnCommandErrorABC from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.configuration.bot_settings import BotSettings +from bot_core.exception.check_error import CheckError class BaseOnCommandErrorEvent(OnCommandErrorABC): @@ -35,6 +36,9 @@ class BaseOnCommandErrorEvent(OnCommandErrorABC): self._t = translate async def on_command_error(self, ctx: Context, error: CommandError): + if isinstance(error, CheckError): + return + error = getattr(error, 'original', error) uid = uuid.uuid4() self._logger.error(__name__, f'Got error: {type(error).__name__} UID: {uid}') From 7173dee28d9e778e08ea8f5ef14a355e6e77ff73 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 13 Nov 2022 11:58:09 +0100 Subject: [PATCH 189/275] Added event checks #114 --- .../bot_core/core_extension/core_extension.py | 2 ++ kdb-bot/src/bot_core/helper/event_checks.py | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 kdb-bot/src/bot_core/helper/event_checks.py diff --git a/kdb-bot/src/bot_core/core_extension/core_extension.py b/kdb-bot/src/bot_core/core_extension/core_extension.py index fba66dd7..a482bcc3 100644 --- a/kdb-bot/src/bot_core/core_extension/core_extension.py +++ b/kdb-bot/src/bot_core/core_extension/core_extension.py @@ -8,6 +8,7 @@ from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings from bot_core.helper.command_checks import CommandChecks +from bot_core.helper.event_checks import EventChecks from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -26,3 +27,4 @@ class CoreExtension(ApplicationExtensionABC): message_service: MessageServiceABC = services.get_service(MessageServiceABC) t: TranslatePipe = services.get_service(TranslatePipe) CommandChecks.init(permissions, client_utils, message_service, t) + EventChecks.init(client_utils) diff --git a/kdb-bot/src/bot_core/helper/event_checks.py b/kdb-bot/src/bot_core/helper/event_checks.py new file mode 100644 index 00000000..9d4e9ccb --- /dev/null +++ b/kdb-bot/src/bot_core/helper/event_checks.py @@ -0,0 +1,31 @@ +from typing import Optional + +from cpl_translation import TranslatePipe +from discord.ext import commands +from discord.ext.commands import Context + +from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC +from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.exception.check_error import CheckError +from modules.permission.abc.permission_service_abc import PermissionServiceABC + + +class EventChecks: + _client_utils: Optional[ClientUtilsServiceABC] = None + + @classmethod + def init( + cls, + client_utils: ClientUtilsServiceABC, + ): + cls._client_utils = client_utils + + @classmethod + def check_is_ready(cls): + async def check_if_bot_is_ready_yet_and_respond(ctx: Context) -> bool: + result = await cls._client_utils.check_if_bot_is_ready_yet_and_respond(ctx) + if not result: + raise CheckError(f'Bot is not ready') + return result + + return commands.check(check_if_bot_is_ready_yet_and_respond) From 5cdf2834cc91a65647d0e77fa0703e5981671b33 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 13 Nov 2022 12:01:34 +0100 Subject: [PATCH 190/275] Added is ready check to events #114 --- .../modules/auto_role/events/auto_role_on_raw_reaction_add.py | 2 ++ .../auto_role/events/auto_role_on_raw_reaction_remove.py | 2 ++ kdb-bot/src/modules/base/events/base_on_member_join_event.py | 2 ++ kdb-bot/src/modules/base/events/base_on_member_remove_event.py | 2 ++ kdb-bot/src/modules/base/events/base_on_message_event.py | 2 ++ .../src/modules/base/events/base_on_voice_state_update_event.py | 2 ++ kdb-bot/src/modules/level/events/level_on_message_event.py | 2 ++ .../modules/level/events/level_on_voice_state_update_event.py | 2 ++ 8 files changed, 16 insertions(+) diff --git a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py index f86de1f0..5893d463 100644 --- a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py +++ b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py @@ -3,6 +3,7 @@ from cpl_discord.events.on_raw_reaction_add_abc import OnRawReactionAddABC from cpl_discord.service import DiscordBotServiceABC from discord import RawReactionActionEvent +from bot_core.helper.event_checks import EventChecks from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from modules.auto_role.helper.reaction_handler import ReactionHandler @@ -26,6 +27,7 @@ class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): self._auto_roles = auto_roles self._reaction_handler = reaction_handler + @EventChecks.check_is_ready() async def on_raw_reaction_add(self, payload: RawReactionActionEvent): self._logger.debug(__name__, f'Module {type(self)} started') diff --git a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py index 30936676..49899a0e 100644 --- a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py +++ b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py @@ -3,6 +3,7 @@ from cpl_discord.events.on_raw_reaction_remove_abc import OnRawReactionRemoveABC from cpl_discord.service import DiscordBotServiceABC from discord import RawReactionActionEvent +from bot_core.helper.event_checks import EventChecks from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from modules.auto_role.helper.reaction_handler import ReactionHandler @@ -26,6 +27,7 @@ class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): self._auto_roles = auto_roles self._reaction_handler = reaction_handler + @EventChecks.check_is_ready() async def on_raw_reaction_remove(self, payload: RawReactionActionEvent): self._logger.debug(__name__, f'Module {type(self)} started') diff --git a/kdb-bot/src/modules/base/events/base_on_member_join_event.py b/kdb-bot/src/modules/base/events/base_on_member_join_event.py index ebd5116e..72dcebce 100644 --- a/kdb-bot/src/modules/base/events/base_on_member_join_event.py +++ b/kdb-bot/src/modules/base/events/base_on_member_join_event.py @@ -9,6 +9,7 @@ from cpl_discord.events import OnMemberJoinABC from cpl_translation import TranslatePipe from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.event_checks import EventChecks from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC @@ -91,6 +92,7 @@ class BaseOnMemberJoinEvent(OnMemberJoinABC): except Exception as e: self._logger.error(__name__, f'Cannot get user {member.id}', e) + @EventChecks.check_is_ready() async def on_member_join(self, member: discord.Member): self._logger.debug(__name__, f'Module {type(self)} started') self._check_for_known_user(member) diff --git a/kdb-bot/src/modules/base/events/base_on_member_remove_event.py b/kdb-bot/src/modules/base/events/base_on_member_remove_event.py index b3d36b8a..e9987fc6 100644 --- a/kdb-bot/src/modules/base/events/base_on_member_remove_event.py +++ b/kdb-bot/src/modules/base/events/base_on_member_remove_event.py @@ -8,6 +8,7 @@ from cpl_discord.events import OnMemberRemoveABC from cpl_translation import TranslatePipe from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.event_checks import EventChecks from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC from bot_data.abc.user_repository_abc import UserRepositoryABC @@ -59,6 +60,7 @@ class BaseOnMemberRemoveEvent(OnMemberRemoveABC): except Exception as e: self._logger.error(__name__, f'Cannot get user {member.id}', e) + @EventChecks.check_is_ready() async def on_member_remove(self, member: discord.Member): self._logger.debug(__name__, f'Module {type(self)} started') await self._remove_user(member) diff --git a/kdb-bot/src/modules/base/events/base_on_message_event.py b/kdb-bot/src/modules/base/events/base_on_message_event.py index 7f6cb926..2d562c66 100644 --- a/kdb-bot/src/modules/base/events/base_on_message_event.py +++ b/kdb-bot/src/modules/base/events/base_on_message_event.py @@ -5,6 +5,7 @@ from cpl_core.database.context import DatabaseContextABC from cpl_discord.events import OnMessageABC from cpl_discord.service import DiscordBotServiceABC +from bot_core.helper.event_checks import EventChecks from bot_core.helper.log_message_helper import LogMessageHelper from bot_core.logging.message_logger import MessageLogger from bot_data.abc.client_repository_abc import ClientRepositoryABC @@ -70,6 +71,7 @@ class BaseOnMessageEvent(OnMessageABC): self._logger.debug(__name__, f'User {user} sent message. xp: from {old_xp} to {user.xp}') + @EventChecks.check_is_ready() async def on_message(self, message: discord.Message): self._logger.debug(__name__, f'Module {type(self)} started') self._logger.info(__name__, f'Received message: {LogMessageHelper.get_log_string(message)}') diff --git a/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py index 5869d446..176977a9 100644 --- a/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py +++ b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event.py @@ -7,6 +7,7 @@ from cpl_core.database.context import DatabaseContextABC from cpl_core.logging import LoggerABC from cpl_discord.events import OnVoiceStateUpdateABC +from bot_core.helper.event_checks import EventChecks from bot_data.abc.known_user_repository_abc import KnownUserRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepositoryABC @@ -83,6 +84,7 @@ class BaseOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): except Exception as e: self._logger.error(__name__, f'Ontime validation failed', e) + @EventChecks.check_is_ready() async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): self._logger.debug(__name__, f'Module {type(self)} started') self._logger.trace(__name__, f'Detected on_voice_state_update {member.id} from {before} to {after}') diff --git a/kdb-bot/src/modules/level/events/level_on_message_event.py b/kdb-bot/src/modules/level/events/level_on_message_event.py index cd69622e..293fb08f 100644 --- a/kdb-bot/src/modules/level/events/level_on_message_event.py +++ b/kdb-bot/src/modules/level/events/level_on_message_event.py @@ -1,6 +1,7 @@ import discord from cpl_discord.events import OnMessageABC +from bot_core.helper.event_checks import EventChecks from bot_core.logging.message_logger import MessageLogger from modules.level.service.level_service import LevelService @@ -16,6 +17,7 @@ class LevelOnMessageEvent(OnMessageABC): self._logger = logger self._level = level + @EventChecks.check_is_ready() async def on_message(self, message: discord.Message): self._logger.debug(__name__, f'Module {type(self)} started') await self._level.check_level(message.author) diff --git a/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py b/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py index c9127e03..910e570a 100644 --- a/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py +++ b/kdb-bot/src/modules/level/events/level_on_voice_state_update_event.py @@ -2,6 +2,7 @@ import discord from cpl_core.logging import LoggerABC from cpl_discord.events import OnVoiceStateUpdateABC +from bot_core.helper.event_checks import EventChecks from modules.level.service.level_service import LevelService @@ -18,6 +19,7 @@ class LevelOnVoiceStateUpdateEvent(OnVoiceStateUpdateABC): self._logger.info(__name__, f'Module {type(self)} loaded') + @EventChecks.check_is_ready() async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): self._logger.debug(__name__, f'Module {type(self)} started') await self._level.check_level(member) From d38fa7775710850b8d07cfb98838fbb9ed1edafa Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 13 Nov 2022 12:07:50 +0100 Subject: [PATCH 191/275] Added checks to commands #114 --- .../auto_role/command/auto_role_group.py | 61 +++---------- .../src/modules/base/command/afk_command.py | 5 +- .../src/modules/base/command/help_command.py | 5 +- .../src/modules/base/command/info_command.py | 5 +- .../src/modules/base/command/ping_command.py | 5 +- .../src/modules/base/command/purge_command.py | 11 +-- .../modules/base/command/shutdown_command.py | 13 +-- .../src/modules/base/command/user_group.py | 11 +-- .../src/modules/level/command/level_group.py | 90 ++++--------------- .../src/modules/stats/command/stats_group.py | 51 +++-------- 10 files changed, 58 insertions(+), 199 deletions(-) diff --git a/kdb-bot/src/modules/auto_role/command/auto_role_group.py b/kdb-bot/src/modules/auto_role/command/auto_role_group.py index 6dbf51c8..ee599473 100644 --- a/kdb-bot/src/modules/auto_role/command/auto_role_group.py +++ b/kdb-bot/src/modules/auto_role/command/auto_role_group.py @@ -13,6 +13,7 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC @@ -56,16 +57,10 @@ class AutoRoleGroup(DiscordCommandABC): @auto_role.command(alias='auto-roles') @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def list(self, ctx: Context, wait: int = None): self._logger.debug(__name__, f'Received command auto-role list {ctx}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command auto-role list') - return if ctx.guild is None: return @@ -95,16 +90,10 @@ class AutoRoleGroup(DiscordCommandABC): @auto_role.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def add(self, ctx: Context, channel: discord.TextChannel, message_id: str): self._logger.debug(__name__, f'Received command auto-role add {ctx} {message_id}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command auto-role add') - return message = List(discord.Message, [message async for message in channel.history(limit=50)]).where(lambda m: m.id == int(message_id)).single_or_default() if message is None: @@ -142,16 +131,10 @@ class AutoRoleGroup(DiscordCommandABC): @auto_role.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def remove(self, ctx: Context, auto_role: int): self._logger.debug(__name__, f'Received command auto-role remove {ctx} {auto_role}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command auto-role remove') - return auto_role_from_db = self._auto_roles.find_auto_role_by_id(auto_role) if auto_role_from_db is None: @@ -183,16 +166,10 @@ class AutoRoleGroup(DiscordCommandABC): @rule.command(alias='rules') @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def list(self, ctx: Context, auto_role: int, wait: int = None): self._logger.debug(__name__, f'Received command auto-role rule list {ctx}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command auto-role rule list') - return embed = discord.Embed( title=self._t.transform('modules.auto_role.list.title'), @@ -227,16 +204,10 @@ class AutoRoleGroup(DiscordCommandABC): @rule.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def add(self, ctx: Context, auto_role: int, emoji_name: str, role_id: str): self._logger.debug(__name__, f'Received command auto-role add {ctx} {auto_role}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command auto-role rule add') - return emoji = discord.utils.get(self._bot.emojis, name=emoji_name) if emoji is None: @@ -300,16 +271,10 @@ class AutoRoleGroup(DiscordCommandABC): @rule.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def remove(self, ctx: Context, auto_role_rule: int): self._logger.debug(__name__, f'Received command auto-role remove {ctx} {auto_role_rule}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command auto-role remove') - return auto_role_from_db = self._auto_roles.get_auto_role_rule_by_id(auto_role_rule) if auto_role_from_db is None: diff --git a/kdb-bot/src/modules/base/command/afk_command.py b/kdb-bot/src/modules/base/command/afk_command.py index 5b21cf75..234c24fe 100644 --- a/kdb-bot/src/modules/base/command/afk_command.py +++ b/kdb-bot/src/modules/base/command/afk_command.py @@ -8,6 +8,7 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from modules.base.configuration.base_server_settings import BaseServerSettings @@ -36,11 +37,9 @@ class AFKCommand(DiscordCommandABC): @commands.hybrid_command() @commands.guild_only() + @CommandChecks.check_is_ready() async def afk(self, ctx: Context): self._logger.debug(__name__, f'Received command afk {ctx}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) settings: BaseServerSettings = self._config.get_configuration(f'BaseServerSettings_{ctx.guild.id}') if ctx.author.voice is None or ctx.author.voice.channel is None: diff --git a/kdb-bot/src/modules/base/command/help_command.py b/kdb-bot/src/modules/base/command/help_command.py index b85dacd8..0c690ecb 100644 --- a/kdb-bot/src/modules/base/command/help_command.py +++ b/kdb-bot/src/modules/base/command/help_command.py @@ -10,6 +10,7 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from modules.base.configuration.base_server_settings import BaseServerSettings @@ -36,11 +37,9 @@ class HelpCommand(DiscordCommandABC): @commands.hybrid_command() @commands.guild_only() + @CommandChecks.check_is_ready() async def help(self, ctx: Context, persistent_flag: str = None): self._logger.debug(__name__, f'Received command help {ctx}:{persistent_flag}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) settings: BaseServerSettings = self._config.get_configuration(f'BaseServerSettings_{ctx.guild.id}') is_persistent = persistent_flag == '--stay' await self._message_service.send_ctx_msg(ctx, settings.help_command_reference_url, is_persistent=is_persistent) diff --git a/kdb-bot/src/modules/base/command/info_command.py b/kdb-bot/src/modules/base/command/info_command.py index 23587778..974bc491 100644 --- a/kdb-bot/src/modules/base/command/info_command.py +++ b/kdb-bot/src/modules/base/command/info_command.py @@ -11,6 +11,7 @@ from discord.ext.commands import Context import bot from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger @@ -38,11 +39,9 @@ class InfoCommand(DiscordCommandABC): @commands.hybrid_command() @commands.guild_only() + @CommandChecks.check_is_ready() async def info(self, ctx: Context, *, wait: int = None): self._logger.debug(__name__, f'Received command info {ctx},{wait}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) client = self._client_utils.get_client(self._bot.user.id, ctx.guild.id) embed = discord.Embed( diff --git a/kdb-bot/src/modules/base/command/ping_command.py b/kdb-bot/src/modules/base/command/ping_command.py index cedbdcef..9ccf1420 100644 --- a/kdb-bot/src/modules/base/command/ping_command.py +++ b/kdb-bot/src/modules/base/command/ping_command.py @@ -6,6 +6,7 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger @@ -31,10 +32,8 @@ class PingCommand(DiscordCommandABC): @commands.hybrid_command() @commands.guild_only() + @CommandChecks.check_is_ready() async def ping(self, ctx: Context): self._logger.debug(__name__, f'Received command ping {ctx}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.pong')) self._logger.trace(__name__, f'Finished ping command') diff --git a/kdb-bot/src/modules/base/command/purge_command.py b/kdb-bot/src/modules/base/command/purge_command.py index 11b90c22..8ec78eff 100644 --- a/kdb-bot/src/modules/base/command/purge_command.py +++ b/kdb-bot/src/modules/base/command/purge_command.py @@ -9,6 +9,7 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.configuration.server_settings import ServerSettings +from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -37,18 +38,12 @@ class PurgeCommand(DiscordCommandABC): @commands.hybrid_command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def purge(self, ctx: Context): self._logger.debug(__name__, f'Received command purge {ctx}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) server_settings: ServerSettings = self._config.get_configuration(f'ServerSettings_{ctx.guild.id}') - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished purge command') - return - await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.moderator.purge_message')) await asyncio.sleep(server_settings.message_delete_timer) try: diff --git a/kdb-bot/src/modules/base/command/shutdown_command.py b/kdb-bot/src/modules/base/command/shutdown_command.py index e2541e8d..f8ebce5d 100644 --- a/kdb-bot/src/modules/base/command/shutdown_command.py +++ b/kdb-bot/src/modules/base/command/shutdown_command.py @@ -11,6 +11,7 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.configuration.bot_settings import BotSettings +from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from modules.permission.abc.permission_service_abc import PermissionServiceABC @@ -43,18 +44,10 @@ class ShutdownCommand(DiscordCommandABC): @commands.hybrid_command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def shutdown(self, ctx: Context): self._logger.debug(__name__, f'Received command shutdown {ctx}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished shutdown command') - return await self._client_utils.presence_game('common.presence.shutdown') await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.admin.shutdown_message')) diff --git a/kdb-bot/src/modules/base/command/user_group.py b/kdb-bot/src/modules/base/command/user_group.py index a2efb8ba..b08bd072 100644 --- a/kdb-bot/src/modules/base/command/user_group.py +++ b/kdb-bot/src/modules/base/command/user_group.py @@ -10,6 +10,7 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from bot_core.pipes.date_time_offset_pipe import DateTimeOffsetPipe from bot_data.abc.server_repository_abc import ServerRepositoryABC @@ -60,16 +61,10 @@ class UserGroup(DiscordCommandABC): @user.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def info(self, ctx: Context, member: Optional[discord.Member] = None, *, wait: int = None): self._logger.debug(__name__, f'Received command user-info {ctx}:{member},{wait}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished purge command') - return if member is None or not isinstance(member, discord.Member): member = ctx.author diff --git a/kdb-bot/src/modules/level/command/level_group.py b/kdb-bot/src/modules/level/command/level_group.py index c8253a2c..502c0936 100644 --- a/kdb-bot/src/modules/level/command/level_group.py +++ b/kdb-bot/src/modules/level/command/level_group.py @@ -12,6 +12,7 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from bot_data.abc.level_repository_abc import LevelRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC @@ -99,16 +100,10 @@ class LevelGroup(DiscordCommandABC): @level.command(alias='levels') @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def list(self, ctx: Context, wait: int = None): self._logger.debug(__name__, f'Received command level list {ctx}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command level list') - return if ctx.guild is None: return @@ -141,16 +136,10 @@ class LevelGroup(DiscordCommandABC): @level.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_admin() async def create(self, ctx: Context, name: str, color: str, min_xp: int, permissions: int): self._logger.debug(__name__, f'Received command level create {ctx}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_admin(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command level remove') - return try: color = hex(discord.Colour.from_str(color).value) @@ -199,19 +188,10 @@ class LevelGroup(DiscordCommandABC): @level.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_admin() async def edit(self, ctx: Context, level: str, name: str = None, color: str = None, min_xp: int = None, permissions: int = None): self._logger.debug(__name__, f'Received command level edit {ctx}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_admin(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command level remove') - return - - if ctx.guild is None: - return server = self._servers.get_server_by_discord_id(ctx.guild.id) level_from_db = self._levels.get_levels_by_server_id(server.server_id).where(lambda l: l.name == level).single_or_default() @@ -248,7 +228,8 @@ class LevelGroup(DiscordCommandABC): self._levels.update_level(level_from_db) self._db.save_changes() await role.edit(name=level_from_db.name, permissions=discord.Permissions(level_from_db.permissions), colour=discord.Colour(int(level_from_db.color, 16))) - self._logger.info(__name__, f'Saved level {level_from_db.name} with color {level_from_db.color}, min_xp {level_from_db.min_xp} and permissions {level_from_db.permissions}') + self._logger.info(__name__, + f'Saved level {level_from_db.name} with color {level_from_db.color}, min_xp {level_from_db.min_xp} and permissions {level_from_db.permissions}') except Exception as e: self._logger.error(__name__, f'Could not save level {level} with color {color}, min_xp {min_xp} and permissions {permissions}', e) else: @@ -271,20 +252,10 @@ class LevelGroup(DiscordCommandABC): @level.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_admin() async def remove(self, ctx: Context, level: str): self._logger.debug(__name__, f'Received command level remove {ctx}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_admin(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command level remove') - return - - if ctx.guild is None: - self._logger.trace(__name__, f'Finished command level remove') - return server = self._servers.get_server_by_discord_id(ctx.guild.id) level_from_db = self._levels.get_levels_by_server_id(server.server_id).where(lambda l: l.name == level).first_or_default() @@ -318,19 +289,10 @@ class LevelGroup(DiscordCommandABC): @level.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def down(self, ctx: Context, member: discord.Member): self._logger.debug(__name__, f'Received command level down {ctx} {member}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command level down') - return - - if ctx.guild is None: - return if member.bot: return @@ -360,19 +322,10 @@ class LevelGroup(DiscordCommandABC): @level.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def up(self, ctx: Context, member: discord.Member): self._logger.debug(__name__, f'Received command level up {ctx} {member}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command level up') - return - - if ctx.guild is None: - return if member.bot: return @@ -402,19 +355,10 @@ class LevelGroup(DiscordCommandABC): @level.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def set(self, ctx: Context, member: discord.Member, level: str): self._logger.debug(__name__, f'Received command level up {ctx} {member}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command level up') - return - - if ctx.guild is None: - return if member.bot: return diff --git a/kdb-bot/src/modules/stats/command/stats_group.py b/kdb-bot/src/modules/stats/command/stats_group.py index 41d40c5e..b035e54a 100644 --- a/kdb-bot/src/modules/stats/command/stats_group.py +++ b/kdb-bot/src/modules/stats/command/stats_group.py @@ -10,6 +10,7 @@ from discord.ext.commands import Context from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger from bot_data.abc.server_repository_abc import ServerRepositoryABC from bot_data.abc.statistic_repository_abc import StatisticRepositoryABC @@ -51,16 +52,10 @@ class StatsGroup(DiscordCommandABC): @stats.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def list(self, ctx: Context, wait: int = None): self._logger.debug(__name__, f'Received command stats list {ctx}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command stats list') - return if ctx.guild is None: return @@ -91,16 +86,10 @@ class StatsGroup(DiscordCommandABC): @stats.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_moderator() async def view(self, ctx: Context, name: str, wait: int = None): self._logger.debug(__name__, f'Received command stats view {ctx}:{name}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_moderator(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command stats view') - return if ctx.guild is None: return @@ -139,16 +128,10 @@ class StatsGroup(DiscordCommandABC): @stats.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_technician() async def add(self, ctx: Context, name: str): self._logger.debug(__name__, f'Received command stats add {ctx}: {name}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_technician(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command stats add') - return if ctx.guild is None: return @@ -161,16 +144,10 @@ class StatsGroup(DiscordCommandABC): @stats.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_technician() async def edit(self, ctx: Context, name: str): self._logger.debug(__name__, f'Received command stats edit {ctx}: {name}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_technician(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command stats edit') - return try: server = self._servers.get_server_by_discord_id(ctx.guild.id) @@ -192,16 +169,10 @@ class StatsGroup(DiscordCommandABC): @stats.command() @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_technician() async def remove(self, ctx: Context, name: str): self._logger.debug(__name__, f'Received command stats remove {ctx}: {name}') - if not await self._client_utils.check_if_bot_is_ready_yet_and_respond(ctx): - return - self._client_utils.received_command(ctx.guild.id) - - if not self._permissions.is_member_technician(ctx.author): - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) - self._logger.trace(__name__, f'Finished command stats remove') - return try: server = self._servers.get_server_by_discord_id(ctx.guild.id) From 026d989789daebd26c6898b631eb4d5124beef9c Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 13 Nov 2022 12:11:01 +0100 Subject: [PATCH 192/275] Improved event checks #114 --- kdb-bot/src/bot_core/abc/client_utils_service_abc.py | 3 +++ kdb-bot/src/bot_core/helper/event_checks.py | 6 +++--- kdb-bot/src/bot_core/service/client_utils_service.py | 10 ++++++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/kdb-bot/src/bot_core/abc/client_utils_service_abc.py b/kdb-bot/src/bot_core/abc/client_utils_service_abc.py index 0345ba36..da5d099a 100644 --- a/kdb-bot/src/bot_core/abc/client_utils_service_abc.py +++ b/kdb-bot/src/bot_core/abc/client_utils_service_abc.py @@ -17,6 +17,9 @@ class ClientUtilsServiceABC(ABC): @abstractmethod def get_client(self, dc_ic: int, guild_id: int): pass + @abstractmethod + async def check_if_bot_is_ready_yet(self) -> bool: pass + @abstractmethod async def check_if_bot_is_ready_yet_and_respond(self, ctx: Context) -> bool: pass diff --git a/kdb-bot/src/bot_core/helper/event_checks.py b/kdb-bot/src/bot_core/helper/event_checks.py index 9d4e9ccb..0eba341c 100644 --- a/kdb-bot/src/bot_core/helper/event_checks.py +++ b/kdb-bot/src/bot_core/helper/event_checks.py @@ -22,10 +22,10 @@ class EventChecks: @classmethod def check_is_ready(cls): - async def check_if_bot_is_ready_yet_and_respond(ctx: Context) -> bool: - result = await cls._client_utils.check_if_bot_is_ready_yet_and_respond(ctx) + async def check_if_bot_is_ready() -> bool: + result = await cls._client_utils.check_if_bot_is_ready() if not result: raise CheckError(f'Bot is not ready') return result - return commands.check(check_if_bot_is_ready_yet_and_respond) + return commands.check(check_if_bot_is_ready) diff --git a/kdb-bot/src/bot_core/service/client_utils_service.py b/kdb-bot/src/bot_core/service/client_utils_service.py index 733e3903..56f52963 100644 --- a/kdb-bot/src/bot_core/service/client_utils_service.py +++ b/kdb-bot/src/bot_core/service/client_utils_service.py @@ -59,14 +59,20 @@ class ClientUtilsService(ClientUtilsServiceABC): client = self._clients.find_client_by_discord_id_and_server_id(self._bot.user.id, server.server_id) return client - async def check_if_bot_is_ready_yet_and_respond(self, ctx: Context) -> bool: + async def check_if_bot_is_ready_yet(self) -> bool: if self._config.get_configuration('IS_READY') == 'true': return True self._logger.debug(__name__, f'Bot is not ready yet {self._t.transform("common.errors.bot_not_ready_yet")}') - await self._message_service.send_ctx_msg(ctx, self._t.transform('common.errors.bot_not_ready_yet'), without_tracking=True) return False + async def check_if_bot_is_ready_yet_and_respond(self, ctx: Context) -> bool: + result = await self.check_if_bot_is_ready_yet() + if result: + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.errors.bot_not_ready_yet'), without_tracking=True) + + return result + async def presence_game(self, t_key: str): if not self._feature_flags.get_flag(FeatureFlagsEnum.presence): return From 905182931c313c2d35ba2fe34553ed4899b58cf4 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 13 Nov 2022 12:15:53 +0100 Subject: [PATCH 193/275] Improved event checks #114 --- kdb-bot/src/bot_core/service/client_utils_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/bot_core/service/client_utils_service.py b/kdb-bot/src/bot_core/service/client_utils_service.py index 56f52963..3d072700 100644 --- a/kdb-bot/src/bot_core/service/client_utils_service.py +++ b/kdb-bot/src/bot_core/service/client_utils_service.py @@ -68,7 +68,7 @@ class ClientUtilsService(ClientUtilsServiceABC): async def check_if_bot_is_ready_yet_and_respond(self, ctx: Context) -> bool: result = await self.check_if_bot_is_ready_yet() - if result: + if not result: await self._message_service.send_ctx_msg(ctx, self._t.transform('common.errors.bot_not_ready_yet'), without_tracking=True) return result From 49d9509255bbf0747e9f7511e80f070fcbe839eb Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 13 Nov 2022 12:32:25 +0100 Subject: [PATCH 194/275] Added logic to send mods a message when member joins help channel #113 --- kdb-bot/src/bot/config | 2 +- kdb-bot/src/bot/translation/de.json | 1 + kdb-bot/src/modules/base/base_module.py | 2 + .../configuration/base_server_settings.py | 6 +++ ...n_voice_state_update_event_help_channel.py | 53 +++++++++++++++++++ 5 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 kdb-bot/src/modules/base/events/base_on_voice_state_update_event_help_channel.py diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 6f63688f..bd8d3a5d 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 6f63688fd914ad6fb3fc5249e4b3763dc261997a +Subproject commit bd8d3a5dad13e0fdcad79b767c032997b716b1a4 diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index b3bbf9eb..5b675289 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -139,6 +139,7 @@ "goodbye_message": "Schade das du uns so schnell verlässt :(", "afk_command_channel_missing_message": "Zu unfähig einem Sprachkanal beizutreten?", "afk_command_move_message": "Ich verschiebe dich ja schon... (◔_◔)", + "member_joined_help_voice_channel": "{} braucht hilfe, bitte kümmer dich drum :D", "pong": "Pong", "info": { "title": "Gismo", diff --git a/kdb-bot/src/modules/base/base_module.py b/kdb-bot/src/modules/base/base_module.py index 4383e58a..8da27656 100644 --- a/kdb-bot/src/modules/base/base_module.py +++ b/kdb-bot/src/modules/base/base_module.py @@ -21,6 +21,7 @@ from modules.base.events.base_on_member_join_event import BaseOnMemberJoinEvent from modules.base.events.base_on_member_remove_event import BaseOnMemberRemoveEvent from modules.base.events.base_on_message_event import BaseOnMessageEvent from modules.base.events.base_on_voice_state_update_event import BaseOnVoiceStateUpdateEvent +from modules.base.events.base_on_voice_state_update_event_help_channel import BaseOnVoiceStateUpdateEventHelpChannel from modules.base.service.base_helper_service import BaseHelperService @@ -51,3 +52,4 @@ class BaseModule(ModuleABC): self._dc.add_event(DiscordEventTypesEnum.on_member_remove.value, BaseOnMemberRemoveEvent) self._dc.add_event(DiscordEventTypesEnum.on_message.value, BaseOnMessageEvent) self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEvent) + self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEventHelpChannel) diff --git a/kdb-bot/src/modules/base/configuration/base_server_settings.py b/kdb-bot/src/modules/base/configuration/base_server_settings.py index d2875f54..578839b9 100644 --- a/kdb-bot/src/modules/base/configuration/base_server_settings.py +++ b/kdb-bot/src/modules/base/configuration/base_server_settings.py @@ -17,6 +17,7 @@ class BaseServerSettings(ConfigurationModelABC): self._afk_channel_ids: List[int] = List(int) self._afk_command_channel_id: int = 0 self._help_command_reference_url: str = '' + self._help_voice_channel_id: int = 0 @property def id(self) -> int: @@ -46,6 +47,10 @@ class BaseServerSettings(ConfigurationModelABC): def help_command_reference_url(self) -> str: return self._help_command_reference_url + @property + def help_voice_channel_id(self) -> int: + return self._help_voice_channel_id + def from_dict(self, settings: dict): try: self._id = int(settings['Id']) @@ -56,6 +61,7 @@ class BaseServerSettings(ConfigurationModelABC): self._afk_channel_ids.append(int(index)) self._afk_command_channel_id = settings['AFKCommandChannelId'] self._help_command_reference_url = settings['HelpCommandReferenceUrl'] + self._help_voice_channel_id = settings['HelpVoiceChannelId'] except Exception as e: Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') diff --git a/kdb-bot/src/modules/base/events/base_on_voice_state_update_event_help_channel.py b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event_help_channel.py new file mode 100644 index 00000000..6f9e6f4d --- /dev/null +++ b/kdb-bot/src/modules/base/events/base_on_voice_state_update_event_help_channel.py @@ -0,0 +1,53 @@ +import discord +from cpl_core.configuration import ConfigurationABC +from cpl_core.logging import LoggerABC +from cpl_discord.events import OnVoiceStateUpdateABC +from cpl_translation import TranslatePipe + +from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.event_checks import EventChecks +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from modules.base.abc.base_helper_abc import BaseHelperABC +from modules.base.configuration.base_server_settings import BaseServerSettings +from modules.permission.abc.permission_service_abc import PermissionServiceABC + + +class BaseOnVoiceStateUpdateEventHelpChannel(OnVoiceStateUpdateABC): + + def __init__( + self, + config: ConfigurationABC, + logger: LoggerABC, + base_helper: BaseHelperABC, + servers: ServerRepositoryABC, + permissions: PermissionServiceABC, + message_service: MessageServiceABC, + t: TranslatePipe, + ): + OnVoiceStateUpdateABC.__init__(self) + self._config = config + self._logger = logger + self._base_helper = base_helper + self._servers = servers + self._permissions = permissions + self._message_service = message_service + self._t = t + + self._logger.info(__name__, f'Module {type(self)} loaded') + + @EventChecks.check_is_ready() + async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): + self._logger.debug(__name__, f'Module {type(self)} started') + server = self._servers.get_server_by_discord_id(member.guild.id) + settings: BaseServerSettings = self._base_helper.get_config(server.discord_server_id) + if after.channel is None or after.channel.id != settings.help_voice_channel_id: + return + + mods = [*self._permissions.get_admins(member.guild.id), *self._permissions.get_moderators(member.guild.id)] + for a in mods: + await self._message_service.send_dm_message( + self._t.transform('modules.base.member_joined_help_voice_channel').format(member.name), + a, + ) + + self._logger.debug(__name__, f'Module {type(self)} stopped') From 87435614db709b23544ab0033ef17ed6a7342133 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Mon, 14 Nov 2022 20:10:44 +0100 Subject: [PATCH 195/275] Fixed angular setup for windoof and linux --- kdb-web/package.json | 5 ++--- kdb-web/tsconfig.json | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/kdb-web/package.json b/kdb-web/package.json index 3e87f4ec..c6c72b80 100644 --- a/kdb-web/package.json +++ b/kdb-web/package.json @@ -1,9 +1,9 @@ { "name": "kdb-web", - "version": "0.3.dev70", + "version": "0.3.0", "scripts": { "ng": "ng", - "update-version": "ts-node -O '{\"module\": \"commonjs\"}' update-version.ts", + "update-version": "ts-node-esm update-version.ts", "prestart": "npm run update-version", "start": "ng serve", "prebuild": "npm run update-version", @@ -48,7 +48,6 @@ "karma-coverage": "~2.2.0", "karma-jasmine": "~5.0.0", "karma-jasmine-html-reporter": "~1.7.0", - "ts-node": "~8.3.0", "typescript": "~4.7.2" } } \ No newline at end of file diff --git a/kdb-web/tsconfig.json b/kdb-web/tsconfig.json index ff06eae1..e541544c 100644 --- a/kdb-web/tsconfig.json +++ b/kdb-web/tsconfig.json @@ -1,5 +1,10 @@ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { + "ts-node": { + "compilerOptions": { + "module": "CommonJS" + } + }, "compileOnSave": false, "compilerOptions": { "baseUrl": "./", From 53604706c209815424b16bf79910fa7b0e8e6ae1 Mon Sep 17 00:00:00 2001 From: Nick Jungmann Date: Mon, 14 Nov 2022 22:29:43 +0100 Subject: [PATCH 196/275] Added technician module #44 --- kdb-bot/cpl-workspace.json | 3 +- kdb-bot/src/bot/config | 2 +- kdb-bot/src/bot/module_list.py | 2 + kdb-bot/src/bot/translation/de.json | 9 ++-- kdb-bot/src/bot_api/config | 2 +- kdb-bot/src/modules/base/base_module.py | 4 -- kdb-bot/src/modules/technician/__init__.py | 1 + .../modules/technician/command/__init__.py | 0 .../command/restart_command.py | 4 +- .../command/shutdown_command.py | 4 +- .../src/modules/technician/technician.json | 46 +++++++++++++++++++ .../modules/technician/technician_module.py | 27 +++++++++++ 12 files changed, 87 insertions(+), 17 deletions(-) create mode 100644 kdb-bot/src/modules/technician/__init__.py create mode 100644 kdb-bot/src/modules/technician/command/__init__.py rename kdb-bot/src/modules/{base => technician}/command/restart_command.py (95%) rename kdb-bot/src/modules/{base => technician}/command/shutdown_command.py (95%) create mode 100644 kdb-bot/src/modules/technician/technician.json create mode 100644 kdb-bot/src/modules/technician/technician_module.py diff --git a/kdb-bot/cpl-workspace.json b/kdb-bot/cpl-workspace.json index 6587cbf6..ac1c4822 100644 --- a/kdb-bot/cpl-workspace.json +++ b/kdb-bot/cpl-workspace.json @@ -13,6 +13,7 @@ "level": "src/modules/level/level.json", "permission": "src/modules/permission/permission.json", "stats": "src/modules/stats/stats.json", + "technician": "src/modules/technician/technician.json", "get-version": "tools/get_version/get-version.json", "post-build": "tools/post_build/post-build.json", "set-version": "tools/set_version/set-version.json" @@ -20,10 +21,8 @@ "Scripts": { "sv": "cpl set-version", "set-version": "cpl run set-version $ARGS; echo '';", - "gv": "cpl get-version", "get-version": "export VERSION=$(cpl run get-version); echo $VERSION;", - "pre-build": "cpl set-version $ARGS", "post-build": "cpl run post-build", diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index bd8d3a5d..7eeac4a3 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit bd8d3a5dad13e0fdcad79b767c032997b716b1a4 +Subproject commit 7eeac4a343fcefb0aaff63a8fd28d35b46930f9a diff --git a/kdb-bot/src/bot/module_list.py b/kdb-bot/src/bot/module_list.py index 3eb60cd3..cde01cee 100644 --- a/kdb-bot/src/bot/module_list.py +++ b/kdb-bot/src/bot/module_list.py @@ -11,6 +11,7 @@ from modules.database.database_module import DatabaseModule from modules.level.level_module import LevelModule from modules.permission.permission_module import PermissionModule from modules.stats.stats_module import StatsModule +from modules.technician.technician_module import TechnicianModule class ModuleList: @@ -28,6 +29,7 @@ class ModuleList: PermissionModule, ApiModule, StatsModule, + TechnicianModule, # has to be last! BootLogModule, CoreExtensionModule, diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 5b675289..986e5de0 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -72,11 +72,6 @@ } }, "modules": { - "admin": { - "restart_message": "Bin gleich wieder da :D", - "shutdown_message": "Trauert nicht um mich, es war eine logische Entscheidung. Das Wohl von Vielen, es wiegt schwerer als das Wohl von Wenigen oder eines Einzelnen. Ich war es und ich werde es immer sein, Euer Freund. Lebt lange und in Frieden :)", - "deploy_message": "Der neue Stand wurde hochgeladen." - }, "auto_role": { "list": { "title": "Beobachtete Nachrichten:", @@ -249,6 +244,10 @@ "failed": "Statistik kann nicht gelöscht werden :(", "success": "Statistik wurde gelöscht :D" } + }, + "technician": { + "restart_message": "Bin gleich wieder da :D", + "shutdown_message": "Trauert nicht um mich, es war eine logische Entscheidung. Das Wohl von Vielen, es wiegt schwerer als das Wohl von Wenigen oder eines Einzelnen. Ich war es und ich werde es immer sein, Euer Freund. Lebt lange und in Frieden :)" } }, "api": { diff --git a/kdb-bot/src/bot_api/config b/kdb-bot/src/bot_api/config index 98303ffd..43a44b31 160000 --- a/kdb-bot/src/bot_api/config +++ b/kdb-bot/src/bot_api/config @@ -1 +1 @@ -Subproject commit 98303ffd45445eecfad57f8b1be86729de3661d2 +Subproject commit 43a44b31f2efc644baadbf830b6414bab085fdea diff --git a/kdb-bot/src/modules/base/base_module.py b/kdb-bot/src/modules/base/base_module.py index 8da27656..cacde84b 100644 --- a/kdb-bot/src/modules/base/base_module.py +++ b/kdb-bot/src/modules/base/base_module.py @@ -12,8 +12,6 @@ from modules.base.command.help_command import HelpCommand from modules.base.command.info_command import InfoCommand from modules.base.command.ping_command import PingCommand from modules.base.command.purge_command import PurgeCommand -from modules.base.command.restart_command import RestartCommand -from modules.base.command.shutdown_command import ShutdownCommand from modules.base.command.user_group import UserGroup from modules.base.events.base_on_command_error_event import BaseOnCommandErrorEvent from modules.base.events.base_on_command_event import BaseOnCommandEvent @@ -41,8 +39,6 @@ class BaseModule(ModuleABC): self._dc.add_command(InfoCommand) self._dc.add_command(PingCommand) - self._dc.add_command(RestartCommand) - self._dc.add_command(ShutdownCommand) self._dc.add_command(PurgeCommand) self._dc.add_command(UserGroup) # events diff --git a/kdb-bot/src/modules/technician/__init__.py b/kdb-bot/src/modules/technician/__init__.py new file mode 100644 index 00000000..ad5eca30 --- /dev/null +++ b/kdb-bot/src/modules/technician/__init__.py @@ -0,0 +1 @@ +# imports: diff --git a/kdb-bot/src/modules/technician/command/__init__.py b/kdb-bot/src/modules/technician/command/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kdb-bot/src/modules/base/command/restart_command.py b/kdb-bot/src/modules/technician/command/restart_command.py similarity index 95% rename from kdb-bot/src/modules/base/command/restart_command.py rename to kdb-bot/src/modules/technician/command/restart_command.py index c1fd3ae5..0a14e41b 100644 --- a/kdb-bot/src/modules/base/command/restart_command.py +++ b/kdb-bot/src/modules/technician/command/restart_command.py @@ -44,13 +44,13 @@ class RestartCommand(DiscordCommandABC): @commands.hybrid_command() @commands.guild_only() @CommandChecks.check_is_ready() - @CommandChecks.check_is_member_moderator() + @CommandChecks.check_is_member_technician() async def restart(self, ctx: Context): self._logger.debug(__name__, f'Received command restart {ctx}') self._config.add_configuration('IS_RESTART', 'true') await self._client_utils.presence_game('common.presence.restart') - await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.admin.restart_message')) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.technician.restart_message')) await asyncio.sleep(self._settings.wait_for_restart) await self._bot.stop_async() diff --git a/kdb-bot/src/modules/base/command/shutdown_command.py b/kdb-bot/src/modules/technician/command/shutdown_command.py similarity index 95% rename from kdb-bot/src/modules/base/command/shutdown_command.py rename to kdb-bot/src/modules/technician/command/shutdown_command.py index f8ebce5d..f81d127a 100644 --- a/kdb-bot/src/modules/base/command/shutdown_command.py +++ b/kdb-bot/src/modules/technician/command/shutdown_command.py @@ -45,12 +45,12 @@ class ShutdownCommand(DiscordCommandABC): @commands.hybrid_command() @commands.guild_only() @CommandChecks.check_is_ready() - @CommandChecks.check_is_member_moderator() + @CommandChecks.check_is_member_technician() async def shutdown(self, ctx: Context): self._logger.debug(__name__, f'Received command shutdown {ctx}') await self._client_utils.presence_game('common.presence.shutdown') - await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.admin.shutdown_message')) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.technician.shutdown_message')) await asyncio.sleep(self._settings.wait_for_shutdown) await self._bot.stop_async() diff --git a/kdb-bot/src/modules/technician/technician.json b/kdb-bot/src/modules/technician/technician.json new file mode 100644 index 00000000..a1a3340e --- /dev/null +++ b/kdb-bot/src/modules/technician/technician.json @@ -0,0 +1,46 @@ +{ + "ProjectSettings": { + "Name": "technician", + "Version": { + "Major": "0", + "Minor": "0", + "Micro": "0" + }, + "Author": "", + "AuthorEmail": "", + "Description": "", + "LongDescription": "", + "URL": "", + "CopyrightDate": "", + "CopyrightName": "", + "LicenseName": "", + "LicenseDescription": "", + "Dependencies": [ + "cpl-core>=2022.10.0.post7" + ], + "DevDependencies": [ + "cpl-cli>=2022.10.0" + ], + "PythonVersion": ">=3.10.6", + "PythonPath": { + "win32": "" + }, + "Classifiers": [] + }, + "BuildSettings": { + "ProjectType": "library", + "SourcePath": "", + "OutputPath": "../../dist", + "Main": "technician.main", + "EntryPoint": "technician", + "IncludePackageData": false, + "Included": [], + "Excluded": [ + "*/__pycache__", + "*/logs", + "*/tests" + ], + "PackageData": {}, + "ProjectReferences": [] + } +} \ No newline at end of file diff --git a/kdb-bot/src/modules/technician/technician_module.py b/kdb-bot/src/modules/technician/technician_module.py new file mode 100644 index 00000000..089e36de --- /dev/null +++ b/kdb-bot/src/modules/technician/technician_module.py @@ -0,0 +1,27 @@ +from cpl_core.configuration import ConfigurationABC +from cpl_core.dependency_injection import ServiceCollectionABC +from cpl_core.environment import ApplicationEnvironmentABC +from cpl_discord.service.discord_collection_abc import DiscordCollectionABC + +from bot_core.abc.module_abc import ModuleABC +from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum +from modules.base.abc.base_helper_abc import BaseHelperABC +from modules.technician.command.restart_command import RestartCommand +from modules.technician.command.shutdown_command import ShutdownCommand +from modules.base.service.base_helper_service import BaseHelperService + + +class TechnicianModule(ModuleABC): + + def __init__(self, dc: DiscordCollectionABC): + ModuleABC.__init__(self, dc, FeatureFlagsEnum.base_module) + + def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): + pass + + def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): + services.add_transient(BaseHelperABC, BaseHelperService) + # commands + self._dc.add_command(RestartCommand) + self._dc.add_command(ShutdownCommand) + # events From 2c7f4647af237903aaba3235b1985c21fd8c6903 Mon Sep 17 00:00:00 2001 From: Nick Jungmann Date: Wed, 16 Nov 2022 21:04:07 +0100 Subject: [PATCH 197/275] [WIP] Added log command #44 --- .../bot_core/abc/custom_file_logger_abc.py | 8 +- .../modules/technician/command/log_command.py | 95 +++++++++++++++++++ .../modules/technician/technician_module.py | 2 + 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 kdb-bot/src/modules/technician/command/log_command.py diff --git a/kdb-bot/src/bot_core/abc/custom_file_logger_abc.py b/kdb-bot/src/bot_core/abc/custom_file_logger_abc.py index c7354f65..d1cb8160 100644 --- a/kdb-bot/src/bot_core/abc/custom_file_logger_abc.py +++ b/kdb-bot/src/bot_core/abc/custom_file_logger_abc.py @@ -13,10 +13,14 @@ class CustomFileLoggerABC(Logger, ABC): @abstractmethod def __init__(self, key: str, config: ConfigurationABC, time_format: TimeFormatSettings, env: ApplicationEnvironmentABC): self._key = key - settings: LoggingSettings = config.get_configuration(f'{FileLoggingSettings.__name__}_{key}') - Logger.__init__(self, settings, time_format, env) + self._settings: LoggingSettings = config.get_configuration(f'{FileLoggingSettings.__name__}_{key}') + Logger.__init__(self, self._settings, time_format, env) self._begin_log() + @property + def settings(self) -> LoggingSettings: + return self._settings + def _begin_log(self): console_level = self._console.value self._console = LoggingLevelEnum.OFF diff --git a/kdb-bot/src/modules/technician/command/log_command.py b/kdb-bot/src/modules/technician/command/log_command.py new file mode 100644 index 00000000..d7395d46 --- /dev/null +++ b/kdb-bot/src/modules/technician/command/log_command.py @@ -0,0 +1,95 @@ +import os +from datetime import datetime + +from cpl_core.dependency_injection import ServiceProviderABC +from cpl_core.logging import LoggingSettings +from cpl_discord.command import DiscordCommandABC +from cpl_query.extension import List +from cpl_translation import TranslatePipe +from discord.ext import commands +from discord.ext.commands import Context + +from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC +from bot_core.abc.custom_file_logger_abc import CustomFileLoggerABC +from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.command_checks import CommandChecks +from bot_core.logging.command_logger import CommandLogger +from modules.permission.abc.permission_service_abc import PermissionServiceABC + + +class LogCommand(DiscordCommandABC): + + def __init__( + self, + logger: CommandLogger, + logging_settings: LoggingSettings, + services: ServiceProviderABC, + message_service: MessageServiceABC, + client_utils: ClientUtilsServiceABC, + translate: TranslatePipe, + permissions: PermissionServiceABC, + ): + DiscordCommandABC.__init__(self) + + self._logger = logger + self._logging_settings = logging_settings + self._services = services + self._message_service = message_service + self._client_utils = client_utils + self._t = translate + self._permissions = permissions + + self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') + + def _reduce_path(self, p: str) -> str: + if p.count('/') == 1 or p == '': + return p + + return self._reduce_path(os.path.dirname(p)) + + @commands.hybrid_command() + @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_technician() + async def log(self, ctx: Context, date_from: datetime = datetime.now()): + self._logger.debug(__name__, f'Received command log {ctx}') + + possible_log_paths = List(str) + possible_log_paths.append(self._reduce_path(self._logging_settings.path)) + + file_extensions = List(str) + if '.' in self._logging_settings.filename: + split_filename = self._logging_settings.filename.split(".") + file_extensions.append(f'.{split_filename[len(split_filename) - 1]}') + + for subclass in CustomFileLoggerABC.__subclasses__(): + logger: CustomFileLoggerABC = self._services.get_service(subclass) + if logger is None: + continue + + path = self._reduce_path(logger.settings.path) + if '.' in logger.settings.filename: + split_filename = logger.settings.filename.split(".") + file_extension = f'.{split_filename[len(split_filename) - 1]}' + if file_extension not in file_extensions: + file_extensions.append(file_extension) + + if path in possible_log_paths: + continue + possible_log_paths.append(path) + + files = List(str) + now = datetime.now() + for possible_path in possible_log_paths: + for r, d, f in os.walk(possible_path): + for file in f: + if '.' not in file: + continue + + split_filename = file.split(".") + if f'.{split_filename[len(split_filename) - 1]}' not in file_extensions: + continue + + files.append(os.path.join(r, file)) + + self._logger.trace(__name__, f'Finished log command') diff --git a/kdb-bot/src/modules/technician/technician_module.py b/kdb-bot/src/modules/technician/technician_module.py index 089e36de..5c074ca8 100644 --- a/kdb-bot/src/modules/technician/technician_module.py +++ b/kdb-bot/src/modules/technician/technician_module.py @@ -6,6 +6,7 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from modules.base.abc.base_helper_abc import BaseHelperABC +from modules.technician.command.log_command import LogCommand from modules.technician.command.restart_command import RestartCommand from modules.technician.command.shutdown_command import ShutdownCommand from modules.base.service.base_helper_service import BaseHelperService @@ -24,4 +25,5 @@ class TechnicianModule(ModuleABC): # commands self._dc.add_command(RestartCommand) self._dc.add_command(ShutdownCommand) + self._dc.add_command(LogCommand) # events From 442170eca957c533c7c7ecfb0c03d778d639d3f6 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 17 Nov 2022 16:16:28 +0100 Subject: [PATCH 198/275] Added pings to servers to ping command #117 --- kdb-bot/src/bot/config | 2 +- .../src/modules/base/command/ping_command.py | 33 +++++++++++++++++-- .../configuration/base_server_settings.py | 7 ++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index bd8d3a5d..58934dde 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit bd8d3a5dad13e0fdcad79b767c032997b716b1a4 +Subproject commit 58934dde3ca8446212a0319088b8726527bd89ab diff --git a/kdb-bot/src/modules/base/command/ping_command.py b/kdb-bot/src/modules/base/command/ping_command.py index 9ccf1420..7633d8a0 100644 --- a/kdb-bot/src/modules/base/command/ping_command.py +++ b/kdb-bot/src/modules/base/command/ping_command.py @@ -1,3 +1,4 @@ +import discord from cpl_discord.command import DiscordCommandABC from cpl_discord.service import DiscordBotServiceABC from cpl_translation import TranslatePipe @@ -8,6 +9,10 @@ from bot_core.abc.client_utils_service_abc import ClientUtilsServiceABC from bot_core.abc.message_service_abc import MessageServiceABC from bot_core.helper.command_checks import CommandChecks from bot_core.logging.command_logger import CommandLogger +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from modules.base.abc.base_helper_abc import BaseHelperABC +from modules.base.configuration.base_server_settings import BaseServerSettings +from modules.permission.abc.permission_service_abc import PermissionServiceABC class PingCommand(DiscordCommandABC): @@ -18,7 +23,10 @@ class PingCommand(DiscordCommandABC): message_service: MessageServiceABC, bot: DiscordBotServiceABC, client_utils: ClientUtilsServiceABC, - translate: TranslatePipe + translate: TranslatePipe, + permissions: PermissionServiceABC, + base_helper: BaseHelperABC, + servers: ServerRepositoryABC, ): DiscordCommandABC.__init__(self) @@ -27,13 +35,34 @@ class PingCommand(DiscordCommandABC): self._bot = bot self._client_utils = client_utils self._t = translate + self._permissions = permissions + self._base_helper = base_helper + self._servers = servers self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') + @staticmethod + def _get_ping(url: str) -> float: + from icmplib import ping + ping_result = ping(url, count=4, interval=0.2, privileged=False) + return ping_result.avg_rtt + @commands.hybrid_command() @commands.guild_only() @CommandChecks.check_is_ready() async def ping(self, ctx: Context): self._logger.debug(__name__, f'Received command ping {ctx}') - await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.pong')) + if self._permissions.is_member_technician(ctx.author): + embed = discord.Embed( + title=self._t.transform('modules.base.info.title'), + description=self._t.transform('modules.base.info.description'), + color=int('ef9d0d', 16) + ) + server = self._servers.get_server_by_discord_id(ctx.guild.id) + settings: BaseServerSettings = self._base_helper.get_config(server.discord_server_id) + for server in settings.ping_urls: + embed.add_field(name=server, value=f'{self._get_ping(server)} ms', inline=False) + await self._message_service.send_ctx_msg(ctx, embed) + else: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.pong')) self._logger.trace(__name__, f'Finished ping command') diff --git a/kdb-bot/src/modules/base/configuration/base_server_settings.py b/kdb-bot/src/modules/base/configuration/base_server_settings.py index 578839b9..cf7552ae 100644 --- a/kdb-bot/src/modules/base/configuration/base_server_settings.py +++ b/kdb-bot/src/modules/base/configuration/base_server_settings.py @@ -18,6 +18,7 @@ class BaseServerSettings(ConfigurationModelABC): self._afk_command_channel_id: int = 0 self._help_command_reference_url: str = '' self._help_voice_channel_id: int = 0 + self._ping_urls = List(str) @property def id(self) -> int: @@ -51,6 +52,10 @@ class BaseServerSettings(ConfigurationModelABC): def help_voice_channel_id(self) -> int: return self._help_voice_channel_id + @property + def ping_urls(self) -> List[str]: + return self._ping_urls + def from_dict(self, settings: dict): try: self._id = int(settings['Id']) @@ -62,6 +67,8 @@ class BaseServerSettings(ConfigurationModelABC): self._afk_command_channel_id = settings['AFKCommandChannelId'] self._help_command_reference_url = settings['HelpCommandReferenceUrl'] self._help_voice_channel_id = settings['HelpVoiceChannelId'] + for url in settings['PingURLs']: + self._ping_urls.append(url) except Exception as e: Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings') Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') From d45d787cea4275360aeafc066a3b991dc77e70c0 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 17 Nov 2022 16:35:47 +0100 Subject: [PATCH 199/275] Added logic to add xp on reaction #118 --- kdb-bot/src/bot/config | 2 +- .../src/modules/auto_role/auto_role_module.py | 4 +- .../events/auto_role_on_raw_reaction_add.py | 4 +- .../auto_role_on_raw_reaction_remove.py | 4 +- ...ndler.py => auto_role_reaction_handler.py} | 2 +- kdb-bot/src/modules/base/base_module.py | 6 +++ .../configuration/base_server_settings.py | 6 +++ .../base/events/base_on_raw_reaction_add.py | 36 +++++++++++++ .../events/base_on_raw_reaction_remove.py | 36 +++++++++++++ kdb-bot/src/modules/base/helper/__init__.py | 26 +++++++++ .../base/helper/base_reaction_handler.py | 53 +++++++++++++++++++ 11 files changed, 171 insertions(+), 8 deletions(-) rename kdb-bot/src/modules/auto_role/helper/{reaction_handler.py => auto_role_reaction_handler.py} (98%) create mode 100644 kdb-bot/src/modules/base/events/base_on_raw_reaction_add.py create mode 100644 kdb-bot/src/modules/base/events/base_on_raw_reaction_remove.py create mode 100644 kdb-bot/src/modules/base/helper/__init__.py create mode 100644 kdb-bot/src/modules/base/helper/base_reaction_handler.py diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index bd8d3a5d..c8e3ac09 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit bd8d3a5dad13e0fdcad79b767c032997b716b1a4 +Subproject commit c8e3ac096317cfdafe809398a80cf659189d42a5 diff --git a/kdb-bot/src/modules/auto_role/auto_role_module.py b/kdb-bot/src/modules/auto_role/auto_role_module.py index cf53c6c4..f4528625 100644 --- a/kdb-bot/src/modules/auto_role/auto_role_module.py +++ b/kdb-bot/src/modules/auto_role/auto_role_module.py @@ -9,7 +9,7 @@ from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from modules.auto_role.command.auto_role_group import AutoRoleGroup from modules.auto_role.events.auto_role_on_raw_reaction_add import AutoRoleOnRawReactionAddEvent from modules.auto_role.events.auto_role_on_raw_reaction_remove import AutoRoleOnRawReactionRemoveEvent -from modules.auto_role.helper.reaction_handler import ReactionHandler +from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler class AutoRoleModule(ModuleABC): @@ -21,7 +21,7 @@ class AutoRoleModule(ModuleABC): pass def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): - services.add_transient(ReactionHandler) + services.add_transient(AutoRoleReactionHandler) # commands self._dc.add_command(AutoRoleGroup) # events diff --git a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py index 5893d463..5c891c02 100644 --- a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py +++ b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py @@ -6,7 +6,7 @@ from discord import RawReactionActionEvent from bot_core.helper.event_checks import EventChecks from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC -from modules.auto_role.helper.reaction_handler import ReactionHandler +from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): @@ -17,7 +17,7 @@ class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): bot: DiscordBotServiceABC, servers: ServerRepositoryABC, auto_roles: AutoRoleRepositoryABC, - reaction_handler: ReactionHandler + reaction_handler: AutoRoleReactionHandler ): OnRawReactionAddABC.__init__(self) diff --git a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py index 49899a0e..5e85f740 100644 --- a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py +++ b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py @@ -6,7 +6,7 @@ from discord import RawReactionActionEvent from bot_core.helper.event_checks import EventChecks from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC -from modules.auto_role.helper.reaction_handler import ReactionHandler +from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): @@ -17,7 +17,7 @@ class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): bot: DiscordBotServiceABC, servers: ServerRepositoryABC, auto_roles: AutoRoleRepositoryABC, - reaction_handler: ReactionHandler + reaction_handler: AutoRoleReactionHandler ): OnRawReactionRemoveABC.__init__(self) diff --git a/kdb-bot/src/modules/auto_role/helper/reaction_handler.py b/kdb-bot/src/modules/auto_role/helper/auto_role_reaction_handler.py similarity index 98% rename from kdb-bot/src/modules/auto_role/helper/reaction_handler.py rename to kdb-bot/src/modules/auto_role/helper/auto_role_reaction_handler.py index 56c8df87..713af5cc 100644 --- a/kdb-bot/src/modules/auto_role/helper/reaction_handler.py +++ b/kdb-bot/src/modules/auto_role/helper/auto_role_reaction_handler.py @@ -10,7 +10,7 @@ from bot_data.model.auto_role import AutoRole from bot_data.model.auto_role_rule import AutoRoleRule -class ReactionHandler: +class AutoRoleReactionHandler: def __init__( self, diff --git a/kdb-bot/src/modules/base/base_module.py b/kdb-bot/src/modules/base/base_module.py index 8da27656..c19ca761 100644 --- a/kdb-bot/src/modules/base/base_module.py +++ b/kdb-bot/src/modules/base/base_module.py @@ -20,8 +20,11 @@ from modules.base.events.base_on_command_event import BaseOnCommandEvent from modules.base.events.base_on_member_join_event import BaseOnMemberJoinEvent from modules.base.events.base_on_member_remove_event import BaseOnMemberRemoveEvent from modules.base.events.base_on_message_event import BaseOnMessageEvent +from modules.base.events.base_on_raw_reaction_add import BaseOnRawReactionAddEvent +from modules.base.events.base_on_raw_reaction_remove import BaseOnRawReactionRemoveEvent from modules.base.events.base_on_voice_state_update_event import BaseOnVoiceStateUpdateEvent from modules.base.events.base_on_voice_state_update_event_help_channel import BaseOnVoiceStateUpdateEventHelpChannel +from modules.base.helper.base_reaction_handler import BaseReactionHandler from modules.base.service.base_helper_service import BaseHelperService @@ -35,6 +38,7 @@ class BaseModule(ModuleABC): def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): services.add_transient(BaseHelperABC, BaseHelperService) + services.add_transient(BaseReactionHandler) # commands self._dc.add_command(AFKCommand) self._dc.add_command(HelpCommand) @@ -51,5 +55,7 @@ class BaseModule(ModuleABC): self._dc.add_event(DiscordEventTypesEnum.on_member_join.value, BaseOnMemberJoinEvent) self._dc.add_event(DiscordEventTypesEnum.on_member_remove.value, BaseOnMemberRemoveEvent) self._dc.add_event(DiscordEventTypesEnum.on_message.value, BaseOnMessageEvent) + self._dc.add_event(DiscordEventTypesEnum.on_raw_reaction_add.value, BaseOnRawReactionAddEvent) + self._dc.add_event(DiscordEventTypesEnum.on_raw_reaction_remove.value, BaseOnRawReactionRemoveEvent) self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEvent) self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEventHelpChannel) diff --git a/kdb-bot/src/modules/base/configuration/base_server_settings.py b/kdb-bot/src/modules/base/configuration/base_server_settings.py index 578839b9..bebd14bd 100644 --- a/kdb-bot/src/modules/base/configuration/base_server_settings.py +++ b/kdb-bot/src/modules/base/configuration/base_server_settings.py @@ -13,6 +13,7 @@ class BaseServerSettings(ConfigurationModelABC): self._id: int = 0 self._max_voice_state_hours: int = 0 self._xp_per_message: int = 0 + self._xp_per_reaction: int = 0 self._xp_per_ontime_hour: int = 0 self._afk_channel_ids: List[int] = List(int) self._afk_command_channel_id: int = 0 @@ -31,6 +32,10 @@ class BaseServerSettings(ConfigurationModelABC): def xp_per_message(self) -> int: return self._xp_per_message + @property + def xp_per_reaction(self) -> int: + return self._xp_per_reaction + @property def xp_per_ontime_hour(self) -> int: return self._xp_per_ontime_hour @@ -56,6 +61,7 @@ class BaseServerSettings(ConfigurationModelABC): self._id = int(settings['Id']) self._max_voice_state_hours = int(settings['MaxVoiceStateHours']) self._xp_per_message = int(settings['XpPerMessage']) + self._xp_per_reaction = int(settings['XpPerReaction']) self._xp_per_ontime_hour = int(settings['XpPerOntimeHour']) for index in settings['AFKChannelIds']: self._afk_channel_ids.append(int(index)) diff --git a/kdb-bot/src/modules/base/events/base_on_raw_reaction_add.py b/kdb-bot/src/modules/base/events/base_on_raw_reaction_add.py new file mode 100644 index 00000000..b8fe597b --- /dev/null +++ b/kdb-bot/src/modules/base/events/base_on_raw_reaction_add.py @@ -0,0 +1,36 @@ +from cpl_core.logging import LoggerABC +from cpl_discord.events.on_raw_reaction_add_abc import OnRawReactionAddABC +from cpl_discord.service import DiscordBotServiceABC +from discord import RawReactionActionEvent + +from bot_core.helper.event_checks import EventChecks +from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from modules.base.helper.base_reaction_handler import BaseReactionHandler + + +class BaseOnRawReactionAddEvent(OnRawReactionAddABC): + + def __init__( + self, + logger: LoggerABC, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + auto_roles: AutoRoleRepositoryABC, + reaction_handler: BaseReactionHandler + ): + OnRawReactionAddABC.__init__(self) + + self._logger = logger + self._bot = bot + self._servers = servers + self._auto_roles = auto_roles + self._reaction_handler = reaction_handler + + @EventChecks.check_is_ready() + async def on_raw_reaction_add(self, payload: RawReactionActionEvent): + self._logger.debug(__name__, f'Module {type(self)} started') + + await self._reaction_handler.handle(payload, 'add') + + self._logger.debug(__name__, f'Module {type(self)} stopped') diff --git a/kdb-bot/src/modules/base/events/base_on_raw_reaction_remove.py b/kdb-bot/src/modules/base/events/base_on_raw_reaction_remove.py new file mode 100644 index 00000000..4f2f0bb7 --- /dev/null +++ b/kdb-bot/src/modules/base/events/base_on_raw_reaction_remove.py @@ -0,0 +1,36 @@ +from cpl_core.logging import LoggerABC +from cpl_discord.events.on_raw_reaction_remove_abc import OnRawReactionRemoveABC +from cpl_discord.service import DiscordBotServiceABC +from discord import RawReactionActionEvent + +from bot_core.helper.event_checks import EventChecks +from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from modules.base.helper.base_reaction_handler import BaseReactionHandler + + +class BaseOnRawReactionRemoveEvent(OnRawReactionRemoveABC): + + def __init__( + self, + logger: LoggerABC, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + auto_roles: AutoRoleRepositoryABC, + reaction_handler: BaseReactionHandler, + ): + OnRawReactionRemoveABC.__init__(self) + + self._logger = logger + self._bot = bot + self._servers = servers + self._auto_roles = auto_roles + self._reaction_handler = reaction_handler + + @EventChecks.check_is_ready() + async def on_raw_reaction_remove(self, payload: RawReactionActionEvent): + self._logger.debug(__name__, f'Module {type(self)} started') + + await self._reaction_handler.handle(payload, 'remove') + + self._logger.debug(__name__, f'Module {type(self)} stopped') diff --git a/kdb-bot/src/modules/base/helper/__init__.py b/kdb-bot/src/modules/base/helper/__init__.py new file mode 100644 index 00000000..a19a540f --- /dev/null +++ b/kdb-bot/src/modules/base/helper/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'modules.auto_role.helper' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.3.dev25' + +from collections import namedtuple + + +# imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/base/helper/base_reaction_handler.py b/kdb-bot/src/modules/base/helper/base_reaction_handler.py new file mode 100644 index 00000000..b7b07136 --- /dev/null +++ b/kdb-bot/src/modules/base/helper/base_reaction_handler.py @@ -0,0 +1,53 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC +from cpl_discord.service import DiscordBotServiceABC +from discord import RawReactionActionEvent + +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from modules.base.abc.base_helper_abc import BaseHelperABC +from modules.base.configuration.base_server_settings import BaseServerSettings + + +class BaseReactionHandler: + + def __init__( + self, + logger: LoggerABC, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + users: UserRepositoryABC, + base_helper: BaseHelperABC, + db: DatabaseContextABC, + ): + self._logger = logger + self._bot = bot + self._servers = servers + self._users = users + self._base_helper = base_helper + self._db = db + + async def handle(self, payload: RawReactionActionEvent, r_type=None) -> None: + self._logger.trace(__name__, f'Handle reaction {payload} {r_type}') + + guild = self._bot.get_guild(payload.guild_id) + member = guild.get_member(payload.user_id) + if member is None: + self._logger.warn(__name__, f'User {payload.user_id} in {guild.name} not found - skipping') + return + + server = self._servers.get_server_by_discord_id(guild.id) + user = self._users.get_user_by_discord_id_and_server_id(member.id, server.server_id) + settings: BaseServerSettings = self._base_helper.get_config(guild.id) + + if r_type == 'add': + user.xp += settings.xp_per_reaction + self._users.update_user(user) + self._db.save_changes() + # for future use maybe + # elif r_type == 'remove': + # user.xp -= settings.xp_per_reaction + # self._users.update_user(user) + # self._db.save_changes() + else: + self._logger.warn(__name__, f'Invalid reaction type {r_type}') From 63fe56604430feb69467b93fda843d595ab2aae1 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 17 Nov 2022 16:49:33 +0100 Subject: [PATCH 200/275] Improved /user info command #119 --- kdb-bot/src/bot/config | 2 +- .../src/modules/base/command/user_group.py | 40 ++++++++++++------- .../permission/service/permission_service.py | 3 +- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index bd8d3a5d..c8e3ac09 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit bd8d3a5dad13e0fdcad79b767c032997b716b1a4 +Subproject commit c8e3ac096317cfdafe809398a80cf659189d42a5 diff --git a/kdb-bot/src/modules/base/command/user_group.py b/kdb-bot/src/modules/base/command/user_group.py index b08bd072..712730b8 100644 --- a/kdb-bot/src/modules/base/command/user_group.py +++ b/kdb-bot/src/modules/base/command/user_group.py @@ -62,10 +62,14 @@ class UserGroup(DiscordCommandABC): @user.command() @commands.guild_only() @CommandChecks.check_is_ready() - @CommandChecks.check_is_member_moderator() async def info(self, ctx: Context, member: Optional[discord.Member] = None, *, wait: int = None): self._logger.debug(__name__, f'Received command user-info {ctx}:{member},{wait}') + is_mod = self._permissions.is_member_moderator(ctx.author) + if member is not None and not is_mod: + await self._message_service.send_ctx_msg(ctx, self._t.transform('common.no_permission_message')) + return + if member is None or not isinstance(member, discord.Member): member = ctx.author @@ -94,20 +98,26 @@ class UserGroup(DiscordCommandABC): roles += f'{role.name}\n' embed.add_field(name=self._t.transform('modules.base.user_info.fields.roles'), value=roles, inline=False) - joins_string = '' - for join in joins: - joins_string += f'{self._date.transform(join.joined_on)}\n' - embed.add_field(name=self._t.transform('modules.base.user_info.fields.joins'), value=joins_string) + if is_mod or member == ctx.author: + joins_string = '' + for join in joins: + joins_string += f'{self._date.transform(join.joined_on)}\n' + embed.add_field(name=self._t.transform('modules.base.user_info.fields.joins'), value=joins_string) - lefts_string = '' - for join in joins: - if join.leaved_on is None: - if lefts_string == '': - lefts_string = '/' - continue - lefts_string += f'{self._date.transform(join.leaved_on)}\n' - embed.add_field(name=self._t.transform('modules.base.user_info.fields.lefts'), value=lefts_string) - embed.add_field(name=self._t.transform('modules.base.user_info.fields.warnings'), value=self._t.transform('common.not_implemented_yet'), inline=False) + if is_mod or member == ctx.author: + lefts_string = '' + for join in joins: + if join.leaved_on is None: + if lefts_string == '': + lefts_string = '/' + continue + lefts_string += f'{self._date.transform(join.leaved_on)}\n' - await self._message_service.send_ctx_msg(ctx, embed, wait_before_delete=wait) + embed.add_field(name=self._t.transform('modules.base.user_info.fields.lefts'), value=lefts_string) + + if is_mod or member == ctx.author: + embed.add_field(name=self._t.transform('modules.base.user_info.fields.warnings'), value=self._t.transform('common.not_implemented_yet'), inline=False) + + # send to interaction because of sensitive data + await self._message_service.send_interaction_msg(ctx.interaction, embed, wait_before_delete=wait) self._logger.trace(__name__, f'Finished user-info command') diff --git a/kdb-bot/src/modules/permission/service/permission_service.py b/kdb-bot/src/modules/permission/service/permission_service.py index f43f2f69..825a9ea0 100644 --- a/kdb-bot/src/modules/permission/service/permission_service.py +++ b/kdb-bot/src/modules/permission/service/permission_service.py @@ -128,8 +128,7 @@ class PermissionService(PermissionServiceABC): return member.guild.id in self._admins and member in self._admins[member.guild.id] def is_member_moderator(self, member: discord.Member) -> bool: - return member.guild.id in self._moderators \ - and member in self._moderators[member.guild.id] or self.is_member_admin(member) + return member.guild.id in self._moderators and member in self._moderators[member.guild.id] or self.is_member_admin(member) def is_member_technician(self, member: discord.Member) -> bool: return member in self._technicians From ab2145d5df09f76875524428178e0fa66f4ea5fb Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 17 Nov 2022 19:31:57 +0100 Subject: [PATCH 201/275] Check level for each member after changes #123 --- kdb-bot/src/bot/config | 2 +- kdb-bot/src/modules/level/level_seeder.py | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index bd8d3a5d..c8e3ac09 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit bd8d3a5dad13e0fdcad79b767c032997b716b1a4 +Subproject commit c8e3ac096317cfdafe809398a80cf659189d42a5 diff --git a/kdb-bot/src/modules/level/level_seeder.py b/kdb-bot/src/modules/level/level_seeder.py index 8e6eb9e7..2708324c 100644 --- a/kdb-bot/src/modules/level/level_seeder.py +++ b/kdb-bot/src/modules/level/level_seeder.py @@ -10,16 +10,26 @@ from bot_data.model.level import Level from bot_data.model.server import Server from bot_data.service.level_repository_service import LevelRepositoryService from modules.level.configuration.default_level_settings import DefaultLevelSettings +from modules.level.service.level_service import LevelService class LevelSeeder(DataSeederABC): - def __init__(self, logger: DatabaseLogger, levels: DefaultLevelSettings, level_repo: LevelRepositoryService, servers: ServerRepositoryABC, bot: DiscordBotServiceABC): + def __init__( + self, + logger: DatabaseLogger, + levels: DefaultLevelSettings, + level_repo: LevelRepositoryService, + servers: ServerRepositoryABC, + level: LevelService, + bot: DiscordBotServiceABC + ): DataSeederABC.__init__(self) self._logger = logger self._levels = level_repo self._servers = servers + self._level = level self._bot = bot self._level_header = levels.level_header @@ -87,4 +97,7 @@ class LevelSeeder(DataSeederABC): except Exception as e: self._logger.error(__name__, f'Cannot change position of {role.name}', e) + for m in guild.members: + await self._level.check_level(m) + self._logger.debug(__name__, f'Checked role order') From a46fbcd9fcd578162f8c33e332bab69c4e0efe6c Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 17 Nov 2022 19:57:28 +0100 Subject: [PATCH 202/275] Removed xp when remove reaction #118 --- kdb-bot/src/modules/base/helper/base_reaction_handler.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kdb-bot/src/modules/base/helper/base_reaction_handler.py b/kdb-bot/src/modules/base/helper/base_reaction_handler.py index b7b07136..401f97b1 100644 --- a/kdb-bot/src/modules/base/helper/base_reaction_handler.py +++ b/kdb-bot/src/modules/base/helper/base_reaction_handler.py @@ -44,10 +44,9 @@ class BaseReactionHandler: user.xp += settings.xp_per_reaction self._users.update_user(user) self._db.save_changes() - # for future use maybe - # elif r_type == 'remove': - # user.xp -= settings.xp_per_reaction - # self._users.update_user(user) - # self._db.save_changes() + elif r_type == 'remove': + user.xp -= settings.xp_per_reaction + self._users.update_user(user) + self._db.save_changes() else: self._logger.warn(__name__, f'Invalid reaction type {r_type}') From c438a91b871105c84bf357efeaf9c27f11d12e34 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 17 Nov 2022 16:35:47 +0100 Subject: [PATCH 203/275] Added logic to add xp on reaction #118 --- kdb-bot/src/bot/config | 2 +- .../src/modules/auto_role/auto_role_module.py | 4 +- .../events/auto_role_on_raw_reaction_add.py | 4 +- .../auto_role_on_raw_reaction_remove.py | 4 +- ...ndler.py => auto_role_reaction_handler.py} | 2 +- kdb-bot/src/modules/base/base_module.py | 6 +++ .../configuration/base_server_settings.py | 6 +++ .../base/events/base_on_raw_reaction_add.py | 36 +++++++++++++ .../events/base_on_raw_reaction_remove.py | 36 +++++++++++++ kdb-bot/src/modules/base/helper/__init__.py | 26 +++++++++ .../base/helper/base_reaction_handler.py | 53 +++++++++++++++++++ 11 files changed, 171 insertions(+), 8 deletions(-) rename kdb-bot/src/modules/auto_role/helper/{reaction_handler.py => auto_role_reaction_handler.py} (98%) create mode 100644 kdb-bot/src/modules/base/events/base_on_raw_reaction_add.py create mode 100644 kdb-bot/src/modules/base/events/base_on_raw_reaction_remove.py create mode 100644 kdb-bot/src/modules/base/helper/__init__.py create mode 100644 kdb-bot/src/modules/base/helper/base_reaction_handler.py diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 58934dde..c8e3ac09 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 58934dde3ca8446212a0319088b8726527bd89ab +Subproject commit c8e3ac096317cfdafe809398a80cf659189d42a5 diff --git a/kdb-bot/src/modules/auto_role/auto_role_module.py b/kdb-bot/src/modules/auto_role/auto_role_module.py index cf53c6c4..f4528625 100644 --- a/kdb-bot/src/modules/auto_role/auto_role_module.py +++ b/kdb-bot/src/modules/auto_role/auto_role_module.py @@ -9,7 +9,7 @@ from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from modules.auto_role.command.auto_role_group import AutoRoleGroup from modules.auto_role.events.auto_role_on_raw_reaction_add import AutoRoleOnRawReactionAddEvent from modules.auto_role.events.auto_role_on_raw_reaction_remove import AutoRoleOnRawReactionRemoveEvent -from modules.auto_role.helper.reaction_handler import ReactionHandler +from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler class AutoRoleModule(ModuleABC): @@ -21,7 +21,7 @@ class AutoRoleModule(ModuleABC): pass def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): - services.add_transient(ReactionHandler) + services.add_transient(AutoRoleReactionHandler) # commands self._dc.add_command(AutoRoleGroup) # events diff --git a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py index 5893d463..5c891c02 100644 --- a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py +++ b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_add.py @@ -6,7 +6,7 @@ from discord import RawReactionActionEvent from bot_core.helper.event_checks import EventChecks from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC -from modules.auto_role.helper.reaction_handler import ReactionHandler +from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): @@ -17,7 +17,7 @@ class AutoRoleOnRawReactionAddEvent(OnRawReactionAddABC): bot: DiscordBotServiceABC, servers: ServerRepositoryABC, auto_roles: AutoRoleRepositoryABC, - reaction_handler: ReactionHandler + reaction_handler: AutoRoleReactionHandler ): OnRawReactionAddABC.__init__(self) diff --git a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py index 49899a0e..5e85f740 100644 --- a/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py +++ b/kdb-bot/src/modules/auto_role/events/auto_role_on_raw_reaction_remove.py @@ -6,7 +6,7 @@ from discord import RawReactionActionEvent from bot_core.helper.event_checks import EventChecks from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC from bot_data.abc.server_repository_abc import ServerRepositoryABC -from modules.auto_role.helper.reaction_handler import ReactionHandler +from modules.auto_role.helper.auto_role_reaction_handler import AutoRoleReactionHandler class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): @@ -17,7 +17,7 @@ class AutoRoleOnRawReactionRemoveEvent(OnRawReactionRemoveABC): bot: DiscordBotServiceABC, servers: ServerRepositoryABC, auto_roles: AutoRoleRepositoryABC, - reaction_handler: ReactionHandler + reaction_handler: AutoRoleReactionHandler ): OnRawReactionRemoveABC.__init__(self) diff --git a/kdb-bot/src/modules/auto_role/helper/reaction_handler.py b/kdb-bot/src/modules/auto_role/helper/auto_role_reaction_handler.py similarity index 98% rename from kdb-bot/src/modules/auto_role/helper/reaction_handler.py rename to kdb-bot/src/modules/auto_role/helper/auto_role_reaction_handler.py index 56c8df87..713af5cc 100644 --- a/kdb-bot/src/modules/auto_role/helper/reaction_handler.py +++ b/kdb-bot/src/modules/auto_role/helper/auto_role_reaction_handler.py @@ -10,7 +10,7 @@ from bot_data.model.auto_role import AutoRole from bot_data.model.auto_role_rule import AutoRoleRule -class ReactionHandler: +class AutoRoleReactionHandler: def __init__( self, diff --git a/kdb-bot/src/modules/base/base_module.py b/kdb-bot/src/modules/base/base_module.py index 8da27656..c19ca761 100644 --- a/kdb-bot/src/modules/base/base_module.py +++ b/kdb-bot/src/modules/base/base_module.py @@ -20,8 +20,11 @@ from modules.base.events.base_on_command_event import BaseOnCommandEvent from modules.base.events.base_on_member_join_event import BaseOnMemberJoinEvent from modules.base.events.base_on_member_remove_event import BaseOnMemberRemoveEvent from modules.base.events.base_on_message_event import BaseOnMessageEvent +from modules.base.events.base_on_raw_reaction_add import BaseOnRawReactionAddEvent +from modules.base.events.base_on_raw_reaction_remove import BaseOnRawReactionRemoveEvent from modules.base.events.base_on_voice_state_update_event import BaseOnVoiceStateUpdateEvent from modules.base.events.base_on_voice_state_update_event_help_channel import BaseOnVoiceStateUpdateEventHelpChannel +from modules.base.helper.base_reaction_handler import BaseReactionHandler from modules.base.service.base_helper_service import BaseHelperService @@ -35,6 +38,7 @@ class BaseModule(ModuleABC): def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC): services.add_transient(BaseHelperABC, BaseHelperService) + services.add_transient(BaseReactionHandler) # commands self._dc.add_command(AFKCommand) self._dc.add_command(HelpCommand) @@ -51,5 +55,7 @@ class BaseModule(ModuleABC): self._dc.add_event(DiscordEventTypesEnum.on_member_join.value, BaseOnMemberJoinEvent) self._dc.add_event(DiscordEventTypesEnum.on_member_remove.value, BaseOnMemberRemoveEvent) self._dc.add_event(DiscordEventTypesEnum.on_message.value, BaseOnMessageEvent) + self._dc.add_event(DiscordEventTypesEnum.on_raw_reaction_add.value, BaseOnRawReactionAddEvent) + self._dc.add_event(DiscordEventTypesEnum.on_raw_reaction_remove.value, BaseOnRawReactionRemoveEvent) self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEvent) self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, BaseOnVoiceStateUpdateEventHelpChannel) diff --git a/kdb-bot/src/modules/base/configuration/base_server_settings.py b/kdb-bot/src/modules/base/configuration/base_server_settings.py index cf7552ae..b5838633 100644 --- a/kdb-bot/src/modules/base/configuration/base_server_settings.py +++ b/kdb-bot/src/modules/base/configuration/base_server_settings.py @@ -13,6 +13,7 @@ class BaseServerSettings(ConfigurationModelABC): self._id: int = 0 self._max_voice_state_hours: int = 0 self._xp_per_message: int = 0 + self._xp_per_reaction: int = 0 self._xp_per_ontime_hour: int = 0 self._afk_channel_ids: List[int] = List(int) self._afk_command_channel_id: int = 0 @@ -32,6 +33,10 @@ class BaseServerSettings(ConfigurationModelABC): def xp_per_message(self) -> int: return self._xp_per_message + @property + def xp_per_reaction(self) -> int: + return self._xp_per_reaction + @property def xp_per_ontime_hour(self) -> int: return self._xp_per_ontime_hour @@ -61,6 +66,7 @@ class BaseServerSettings(ConfigurationModelABC): self._id = int(settings['Id']) self._max_voice_state_hours = int(settings['MaxVoiceStateHours']) self._xp_per_message = int(settings['XpPerMessage']) + self._xp_per_reaction = int(settings['XpPerReaction']) self._xp_per_ontime_hour = int(settings['XpPerOntimeHour']) for index in settings['AFKChannelIds']: self._afk_channel_ids.append(int(index)) diff --git a/kdb-bot/src/modules/base/events/base_on_raw_reaction_add.py b/kdb-bot/src/modules/base/events/base_on_raw_reaction_add.py new file mode 100644 index 00000000..b8fe597b --- /dev/null +++ b/kdb-bot/src/modules/base/events/base_on_raw_reaction_add.py @@ -0,0 +1,36 @@ +from cpl_core.logging import LoggerABC +from cpl_discord.events.on_raw_reaction_add_abc import OnRawReactionAddABC +from cpl_discord.service import DiscordBotServiceABC +from discord import RawReactionActionEvent + +from bot_core.helper.event_checks import EventChecks +from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from modules.base.helper.base_reaction_handler import BaseReactionHandler + + +class BaseOnRawReactionAddEvent(OnRawReactionAddABC): + + def __init__( + self, + logger: LoggerABC, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + auto_roles: AutoRoleRepositoryABC, + reaction_handler: BaseReactionHandler + ): + OnRawReactionAddABC.__init__(self) + + self._logger = logger + self._bot = bot + self._servers = servers + self._auto_roles = auto_roles + self._reaction_handler = reaction_handler + + @EventChecks.check_is_ready() + async def on_raw_reaction_add(self, payload: RawReactionActionEvent): + self._logger.debug(__name__, f'Module {type(self)} started') + + await self._reaction_handler.handle(payload, 'add') + + self._logger.debug(__name__, f'Module {type(self)} stopped') diff --git a/kdb-bot/src/modules/base/events/base_on_raw_reaction_remove.py b/kdb-bot/src/modules/base/events/base_on_raw_reaction_remove.py new file mode 100644 index 00000000..4f2f0bb7 --- /dev/null +++ b/kdb-bot/src/modules/base/events/base_on_raw_reaction_remove.py @@ -0,0 +1,36 @@ +from cpl_core.logging import LoggerABC +from cpl_discord.events.on_raw_reaction_remove_abc import OnRawReactionRemoveABC +from cpl_discord.service import DiscordBotServiceABC +from discord import RawReactionActionEvent + +from bot_core.helper.event_checks import EventChecks +from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from modules.base.helper.base_reaction_handler import BaseReactionHandler + + +class BaseOnRawReactionRemoveEvent(OnRawReactionRemoveABC): + + def __init__( + self, + logger: LoggerABC, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + auto_roles: AutoRoleRepositoryABC, + reaction_handler: BaseReactionHandler, + ): + OnRawReactionRemoveABC.__init__(self) + + self._logger = logger + self._bot = bot + self._servers = servers + self._auto_roles = auto_roles + self._reaction_handler = reaction_handler + + @EventChecks.check_is_ready() + async def on_raw_reaction_remove(self, payload: RawReactionActionEvent): + self._logger.debug(__name__, f'Module {type(self)} started') + + await self._reaction_handler.handle(payload, 'remove') + + self._logger.debug(__name__, f'Module {type(self)} stopped') diff --git a/kdb-bot/src/modules/base/helper/__init__.py b/kdb-bot/src/modules/base/helper/__init__.py new file mode 100644 index 00000000..a19a540f --- /dev/null +++ b/kdb-bot/src/modules/base/helper/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" +bot Keksdose bot +~~~~~~~~~~~~~~~~~~~ + +Discord bot for the Keksdose discord Server + +:copyright: (c) 2022 sh-edraft.de +:license: MIT, see LICENSE for more details. + +""" + +__title__ = 'modules.auto_role.helper' +__author__ = 'Sven Heidemann' +__license__ = 'MIT' +__copyright__ = 'Copyright (c) 2022 sh-edraft.de' +__version__ = '0.3.dev25' + +from collections import namedtuple + + +# imports: + +VersionInfo = namedtuple('VersionInfo', 'major minor micro') +version_info = VersionInfo(major='0', minor='3', micro='dev25') diff --git a/kdb-bot/src/modules/base/helper/base_reaction_handler.py b/kdb-bot/src/modules/base/helper/base_reaction_handler.py new file mode 100644 index 00000000..b7b07136 --- /dev/null +++ b/kdb-bot/src/modules/base/helper/base_reaction_handler.py @@ -0,0 +1,53 @@ +from cpl_core.database.context import DatabaseContextABC +from cpl_core.logging import LoggerABC +from cpl_discord.service import DiscordBotServiceABC +from discord import RawReactionActionEvent + +from bot_data.abc.server_repository_abc import ServerRepositoryABC +from bot_data.abc.user_repository_abc import UserRepositoryABC +from modules.base.abc.base_helper_abc import BaseHelperABC +from modules.base.configuration.base_server_settings import BaseServerSettings + + +class BaseReactionHandler: + + def __init__( + self, + logger: LoggerABC, + bot: DiscordBotServiceABC, + servers: ServerRepositoryABC, + users: UserRepositoryABC, + base_helper: BaseHelperABC, + db: DatabaseContextABC, + ): + self._logger = logger + self._bot = bot + self._servers = servers + self._users = users + self._base_helper = base_helper + self._db = db + + async def handle(self, payload: RawReactionActionEvent, r_type=None) -> None: + self._logger.trace(__name__, f'Handle reaction {payload} {r_type}') + + guild = self._bot.get_guild(payload.guild_id) + member = guild.get_member(payload.user_id) + if member is None: + self._logger.warn(__name__, f'User {payload.user_id} in {guild.name} not found - skipping') + return + + server = self._servers.get_server_by_discord_id(guild.id) + user = self._users.get_user_by_discord_id_and_server_id(member.id, server.server_id) + settings: BaseServerSettings = self._base_helper.get_config(guild.id) + + if r_type == 'add': + user.xp += settings.xp_per_reaction + self._users.update_user(user) + self._db.save_changes() + # for future use maybe + # elif r_type == 'remove': + # user.xp -= settings.xp_per_reaction + # self._users.update_user(user) + # self._db.save_changes() + else: + self._logger.warn(__name__, f'Invalid reaction type {r_type}') From fdb358c45e63e2ce60e431d258df48415a5995c4 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 17 Nov 2022 19:57:28 +0100 Subject: [PATCH 204/275] Removed xp when remove reaction #118 --- kdb-bot/src/modules/base/helper/base_reaction_handler.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kdb-bot/src/modules/base/helper/base_reaction_handler.py b/kdb-bot/src/modules/base/helper/base_reaction_handler.py index b7b07136..401f97b1 100644 --- a/kdb-bot/src/modules/base/helper/base_reaction_handler.py +++ b/kdb-bot/src/modules/base/helper/base_reaction_handler.py @@ -44,10 +44,9 @@ class BaseReactionHandler: user.xp += settings.xp_per_reaction self._users.update_user(user) self._db.save_changes() - # for future use maybe - # elif r_type == 'remove': - # user.xp -= settings.xp_per_reaction - # self._users.update_user(user) - # self._db.save_changes() + elif r_type == 'remove': + user.xp -= settings.xp_per_reaction + self._users.update_user(user) + self._db.save_changes() else: self._logger.warn(__name__, f'Invalid reaction type {r_type}') From 7b8dca64bf9f092b324648194a9d7a46db2c06c8 Mon Sep 17 00:00:00 2001 From: Nick Jungmann Date: Thu, 17 Nov 2022 22:45:10 +0100 Subject: [PATCH 205/275] Finished log command #44 --- kdb-bot/src/bot/translation/de.json | 3 ++- kdb-bot/src/bot_core/abc/message_service_abc.py | 2 +- kdb-bot/src/bot_core/service/message_service.py | 6 +++--- kdb-bot/src/modules/technician/command/log_command.py | 11 +++++++++-- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 986e5de0..9398972e 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -247,7 +247,8 @@ }, "technician": { "restart_message": "Bin gleich wieder da :D", - "shutdown_message": "Trauert nicht um mich, es war eine logische Entscheidung. Das Wohl von Vielen, es wiegt schwerer als das Wohl von Wenigen oder eines Einzelnen. Ich war es und ich werde es immer sein, Euer Freund. Lebt lange und in Frieden :)" + "shutdown_message": "Trauert nicht um mich, es war eine logische Entscheidung. Das Wohl von Vielen, es wiegt schwerer als das Wohl von Wenigen oder eines Einzelnen. Ich war es und ich werde es immer sein, Euer Freund. Lebt lange und in Frieden :)", + "log_message": "Hier sind deine Logdateien! :)" } }, "api": { diff --git a/kdb-bot/src/bot_core/abc/message_service_abc.py b/kdb-bot/src/bot_core/abc/message_service_abc.py index 614dd1df..3542e2b7 100644 --- a/kdb-bot/src/bot_core/abc/message_service_abc.py +++ b/kdb-bot/src/bot_core/abc/message_service_abc.py @@ -28,4 +28,4 @@ class MessageServiceABC(ABC): async def send_ctx_msg(self, ctx: Context, message: Union[str, discord.Embed], file: discord.File = None, is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass @abstractmethod - async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True): pass + async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=True, **kwargs): pass diff --git a/kdb-bot/src/bot_core/service/message_service.py b/kdb-bot/src/bot_core/service/message_service.py index 04f33bfb..dcd329a2 100644 --- a/kdb-bot/src/bot_core/service/message_service.py +++ b/kdb-bot/src/bot_core/service/message_service.py @@ -119,7 +119,7 @@ class MessageService(MessageServiceABC): if ctx.guild is not None: await self.delete_message(msg, without_tracking) - async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=False): + async def send_interaction_msg(self, interaction: Interaction, message: Union[str, discord.Embed], is_persistent: bool = False, wait_before_delete: int = None, without_tracking=False, **kwargs): if interaction is None: self._logger.warn(__name__, 'Message context is empty') self._logger.debug(__name__, f'Message: {message}') @@ -128,9 +128,9 @@ class MessageService(MessageServiceABC): self._logger.debug(__name__, f'Try to send message\t\t{message}\n\tto: {interaction.channel}') try: if isinstance(message, discord.Embed): - await interaction.response.send_message(embed=message) + await interaction.response.send_message(embed=message, **kwargs) else: - await interaction.response.send_message(message) + await interaction.response.send_message(message, **kwargs) except Exception as e: self._logger.error(__name__, f'Send message to channel {interaction.channel.id} failed', e) else: diff --git a/kdb-bot/src/modules/technician/command/log_command.py b/kdb-bot/src/modules/technician/command/log_command.py index d7395d46..8d8c7832 100644 --- a/kdb-bot/src/modules/technician/command/log_command.py +++ b/kdb-bot/src/modules/technician/command/log_command.py @@ -1,6 +1,8 @@ import os from datetime import datetime +from zipfile import ZipFile +import discord from cpl_core.dependency_injection import ServiceProviderABC from cpl_core.logging import LoggingSettings from cpl_discord.command import DiscordCommandABC @@ -51,7 +53,7 @@ class LogCommand(DiscordCommandABC): @commands.guild_only() @CommandChecks.check_is_ready() @CommandChecks.check_is_member_technician() - async def log(self, ctx: Context, date_from: datetime = datetime.now()): + async def log(self, ctx: Context): self._logger.debug(__name__, f'Received command log {ctx}') possible_log_paths = List(str) @@ -79,7 +81,6 @@ class LogCommand(DiscordCommandABC): possible_log_paths.append(path) files = List(str) - now = datetime.now() for possible_path in possible_log_paths: for r, d, f in os.walk(possible_path): for file in f: @@ -92,4 +93,10 @@ class LogCommand(DiscordCommandABC): files.append(os.path.join(r, file)) + zip_file = ZipFile('logs.zip', 'w') + files.for_each(lambda x: zip_file.write(x)) + zip_file.close() + await self._message_service.send_interaction_msg(ctx.interaction, self._t.transform('modules.technician.log_message'), file=discord.File(zip_file.filename, 'logs.zip'), ephemeral=True) + os.remove(zip_file.filename) + self._logger.trace(__name__, f'Finished log command') From e6fc41090aa4ece6ea5473c86ce3e1f39425ca31 Mon Sep 17 00:00:00 2001 From: Nick Jungmann Date: Thu, 17 Nov 2022 23:02:27 +0100 Subject: [PATCH 206/275] Refactored code #44 --- kdb-bot/cpl-workspace.json | 2 ++ kdb-bot/src/modules/technician/technician.json | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kdb-bot/cpl-workspace.json b/kdb-bot/cpl-workspace.json index ac1c4822..89d67f88 100644 --- a/kdb-bot/cpl-workspace.json +++ b/kdb-bot/cpl-workspace.json @@ -21,8 +21,10 @@ "Scripts": { "sv": "cpl set-version", "set-version": "cpl run set-version $ARGS; echo '';", + "gv": "cpl get-version", "get-version": "export VERSION=$(cpl run get-version); echo $VERSION;", + "pre-build": "cpl set-version $ARGS", "post-build": "cpl run post-build", diff --git a/kdb-bot/src/modules/technician/technician.json b/kdb-bot/src/modules/technician/technician.json index a1a3340e..9357fda8 100644 --- a/kdb-bot/src/modules/technician/technician.json +++ b/kdb-bot/src/modules/technician/technician.json @@ -22,9 +22,7 @@ "cpl-cli>=2022.10.0" ], "PythonVersion": ">=3.10.6", - "PythonPath": { - "win32": "" - }, + "PythonPath": {}, "Classifiers": [] }, "BuildSettings": { From 47dd6fdc2d84030af7654228ddd31279ae690603 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 18 Nov 2022 09:30:29 +0100 Subject: [PATCH 207/275] Improved build version stuff --- kdb-bot/cpl-workspace.json | 9 +++++---- kdb-bot/tools/set_version/application.py | 8 ++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/kdb-bot/cpl-workspace.json b/kdb-bot/cpl-workspace.json index 89d67f88..81b9c97b 100644 --- a/kdb-bot/cpl-workspace.json +++ b/kdb-bot/cpl-workspace.json @@ -19,7 +19,7 @@ "set-version": "tools/set_version/set-version.json" }, "Scripts": { - "sv": "cpl set-version", + "sv": "cpl set-version $ARGS", "set-version": "cpl run set-version $ARGS; echo '';", "gv": "cpl get-version", @@ -37,9 +37,10 @@ "pre-dev": "cpl build", "dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;", - "docker-build": "cpl b; docker-compose down; docker build -t kdb-bot/kdb-bot:$(cpl gv) .", - "docker-compose": "docker-compose up -d", - "docker": "cpl docker-build; cpl docker-compose;" + "docker-build": "cpl build $ARGS; cd docker; echo docker build -t kdb-bot/kdb-bot:$(cd ../; cpl gv; cd docker;) .; cd ..;", + "dc-up": "docker-compose up -d", + "dc-down": "docker-compose down", + "docker": "cpl dc-down; cpl docker-build; cpl dc-up;" } } } \ No newline at end of file diff --git a/kdb-bot/tools/set_version/application.py b/kdb-bot/tools/set_version/application.py index e722a272..f9b1e711 100644 --- a/kdb-bot/tools/set_version/application.py +++ b/kdb-bot/tools/set_version/application.py @@ -46,7 +46,7 @@ class Application(ApplicationABC): return if len(args) == 1: - suffix = f'.{args[0]}' + suffix = args[0] try: branch = self._git_service.get_active_branch_name() @@ -67,8 +67,12 @@ class Application(ApplicationABC): version[VersionSettingsNameEnum.major.value] = branch.split('.')[0] version[VersionSettingsNameEnum.minor.value] = branch.split('.')[1] if len(branch.split('.')) == 2: - version[VersionSettingsNameEnum.micro.value] = f'0{suffix}' + if suffix == '': + suffix = '0' + version[VersionSettingsNameEnum.micro.value] = f'{suffix}' else: + if not suffix.startswith('.') and suffix != '': + suffix = f'.{suffix}' version[VersionSettingsNameEnum.micro.value] = f'{branch.split(".")[2]}{suffix}' except Exception as e: Console.error(f'Branch {branch} does not contain valid version') From 90011be760899dc551225ce76ae045eb002f6909 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 18 Nov 2022 09:30:44 +0100 Subject: [PATCH 208/275] Updated api config ? --- kdb-bot/src/bot_api/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/bot_api/config b/kdb-bot/src/bot_api/config index 43a44b31..98303ffd 160000 --- a/kdb-bot/src/bot_api/config +++ b/kdb-bot/src/bot_api/config @@ -1 +1 @@ -Subproject commit 43a44b31f2efc644baadbf830b6414bab085fdea +Subproject commit 98303ffd45445eecfad57f8b1be86729de3661d2 From 864d181de063373b42c5940f254f354b56be1ea1 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 18 Nov 2022 09:33:50 +0100 Subject: [PATCH 209/275] Fixed project files --- kdb-bot/src/bot/bot.json | 6 +++--- kdb-bot/src/modules/level/level.json | 4 +--- kdb-bot/src/modules/stats/stats.json | 4 +--- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/kdb-bot/src/bot/bot.json b/kdb-bot/src/bot/bot.json index a830d029..8a2bc135 100644 --- a/kdb-bot/src/bot/bot.json +++ b/kdb-bot/src/bot/bot.json @@ -16,7 +16,7 @@ "LicenseName": "MIT", "LicenseDescription": "MIT, see LICENSE for more details.", "Dependencies": [ - "cpl-core==2022.10.0.post7", + "cpl-core==2022.10.0.post9", "cpl-translation==2022.10.0.post2", "cpl-query==2022.10.0.post2", "cpl-discord==2022.10.0.post6", @@ -59,9 +59,9 @@ "../modules/boot_log/boot-log.json", "../modules/database/database.json", "../modules/level/level.json", - "../modules/permission/level.json", "../modules/permission/permission.json", - "../modules/permission/stats.json" + "../modules/stats/stats.json", + "../modules/technician/technician.json" ] } } \ No newline at end of file diff --git a/kdb-bot/src/modules/level/level.json b/kdb-bot/src/modules/level/level.json index 7ceaa225..c917e226 100644 --- a/kdb-bot/src/modules/level/level.json +++ b/kdb-bot/src/modules/level/level.json @@ -22,9 +22,7 @@ "cpl-cli>=2022.10.1" ], "PythonVersion": ">=3.10.4", - "PythonPath": { - "linux": "" - }, + "PythonPath": {}, "Classifiers": [] }, "BuildSettings": { diff --git a/kdb-bot/src/modules/stats/stats.json b/kdb-bot/src/modules/stats/stats.json index c4fec974..b4f6c717 100644 --- a/kdb-bot/src/modules/stats/stats.json +++ b/kdb-bot/src/modules/stats/stats.json @@ -22,9 +22,7 @@ "cpl-cli>=2022.10.1" ], "PythonVersion": ">=3.10.4", - "PythonPath": { - "linux": "" - }, + "PythonPath": {}, "Classifiers": [] }, "BuildSettings": { From 87350cba1a05951b6bc9233d6fab0831a6922624 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 18 Nov 2022 09:50:06 +0100 Subject: [PATCH 210/275] Moved dockerfile --- kdb-bot/docker | 2 +- kdb-bot/dockerfile | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 kdb-bot/dockerfile diff --git a/kdb-bot/docker b/kdb-bot/docker index b3d5e9fe..48c26839 160000 --- a/kdb-bot/docker +++ b/kdb-bot/docker @@ -1 +1 @@ -Subproject commit b3d5e9feef881960c2e65e6661b36123b4d6fce5 +Subproject commit 48c2683965611c9a96ebbb908f8dcb4d0d7d71f2 diff --git a/kdb-bot/dockerfile b/kdb-bot/dockerfile new file mode 100644 index 00000000..fb342fd2 --- /dev/null +++ b/kdb-bot/dockerfile @@ -0,0 +1,17 @@ +# syntax=docker/dockerfile:1 +FROM python:3.10.4-alpine + +WORKDIR /app +COPY ./dist/bot/build/ . + +RUN python -m pip install --upgrade pip + +RUN apk update +RUN apk add --update alpine-sdk linux-headers +RUN apk add bash +RUN apk add nano + +RUN pip install -r requirements.txt --extra-index-url https://pip.sh-edraft.de +RUN pip install flask[async] + +CMD [ "bash", "/app/bot/bot"] From 25b7b180134648b35127313da035ca4624418ebe Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 18 Nov 2022 09:51:06 +0100 Subject: [PATCH 211/275] Fixed workspace --- kdb-bot/cpl-workspace.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/cpl-workspace.json b/kdb-bot/cpl-workspace.json index 81b9c97b..a741ab73 100644 --- a/kdb-bot/cpl-workspace.json +++ b/kdb-bot/cpl-workspace.json @@ -37,7 +37,7 @@ "pre-dev": "cpl build", "dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;", - "docker-build": "cpl build $ARGS; cd docker; echo docker build -t kdb-bot/kdb-bot:$(cd ../; cpl gv; cd docker;) .; cd ..;", + "docker-build": "cpl build $ARGS; cd docker; docker build -t kdb-bot/kdb-bot:$(cd ../; cpl gv; cd docker;) .; cd ..;", "dc-up": "docker-compose up -d", "dc-down": "docker-compose down", "docker": "cpl dc-down; cpl docker-build; cpl dc-up;" From b0459567f467f62cc9696b7f945d0525ea70269d Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 18 Nov 2022 09:51:06 +0100 Subject: [PATCH 212/275] Fixed workspace --- kdb-bot/cpl-workspace.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/cpl-workspace.json b/kdb-bot/cpl-workspace.json index 81b9c97b..f16de580 100644 --- a/kdb-bot/cpl-workspace.json +++ b/kdb-bot/cpl-workspace.json @@ -37,7 +37,7 @@ "pre-dev": "cpl build", "dev": "export KDB_ENVIRONMENT=development; export KDB_NAME=KDB-Dev; cpl start;", - "docker-build": "cpl build $ARGS; cd docker; echo docker build -t kdb-bot/kdb-bot:$(cd ../; cpl gv; cd docker;) .; cd ..;", + "docker-build": "cpl build $ARGS; docker build -t kdb-bot/kdb-bot:$(cpl gv) .;", "dc-up": "docker-compose up -d", "dc-down": "docker-compose down", "docker": "cpl dc-down; cpl docker-build; cpl dc-up;" From a7dbc75d2e8e9b1dbc400cc28b9a7908057648a8 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 18 Nov 2022 09:58:05 +0100 Subject: [PATCH 213/275] Updated configs --- kdb-bot/src/bot/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index c8e3ac09..2113e7a9 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit c8e3ac096317cfdafe809398a80cf659189d42a5 +Subproject commit 2113e7a98eb309307bdcb067403760bc96f69268 From ec7aeb871230311011528da56d3fef0ad135b3dd Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 18 Nov 2022 10:16:59 +0100 Subject: [PATCH 214/275] Added icmplib --- kdb-bot/src/bot/bot.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kdb-bot/src/bot/bot.json b/kdb-bot/src/bot/bot.json index 8a2bc135..2d447472 100644 --- a/kdb-bot/src/bot/bot.json +++ b/kdb-bot/src/bot/bot.json @@ -27,7 +27,8 @@ "waitress==2.1.2", "Flask-SocketIO==5.3.1", "eventlet==0.33.1", - "requests-oauthlib==1.3.1" + "requests-oauthlib==1.3.1", + "icmplib==3.0.3" ], "DevDependencies": [ "cpl-cli==2022.10.0" From d3279eb7c7292b13a32569134fba122a067e5f0d Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 18 Nov 2022 10:23:40 +0100 Subject: [PATCH 215/275] Updated config --- kdb-bot/src/bot/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 2113e7a9..43ae9106 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 2113e7a98eb309307bdcb067403760bc96f69268 +Subproject commit 43ae9106e2032d54cc33de3521fdb08742ab4ecd From f5a71a8450dffc50682e106c042d148e4363b741 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 18 Nov 2022 14:14:01 +0100 Subject: [PATCH 216/275] Fixed some on member join stuff --- kdb-bot/src/modules/base/events/base_on_member_join_event.py | 2 +- kdb-bot/src/modules/level/service/level_service.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/kdb-bot/src/modules/base/events/base_on_member_join_event.py b/kdb-bot/src/modules/base/events/base_on_member_join_event.py index 72dcebce..0e483221 100644 --- a/kdb-bot/src/modules/base/events/base_on_member_join_event.py +++ b/kdb-bot/src/modules/base/events/base_on_member_join_event.py @@ -77,10 +77,10 @@ class BaseOnMemberJoinEvent(OnMemberJoinABC): try: server = self._servers.get_server_by_discord_id(member.guild.id) - user = self._users.find_user_by_discord_id_and_server_id(member.id, server.server_id) if user is not None: self._user_joins.add_user_joined_server(UserJoinedServer(user, datetime.now())) + self._db.save_changes() return self._logger.debug(__name__, f'Add user: {member.id}') diff --git a/kdb-bot/src/modules/level/service/level_service.py b/kdb-bot/src/modules/level/service/level_service.py index f47102b9..eb9756ab 100644 --- a/kdb-bot/src/modules/level/service/level_service.py +++ b/kdb-bot/src/modules/level/service/level_service.py @@ -88,5 +88,6 @@ class LevelService: user = self._users.find_user_by_discord_id_and_server_id(member.id, server.server_id) if user is None: self._logger.warn(__name__, f'User not found {member.guild.name}@{member.name}') + return await self.set_level(user) From 9b5033b80e6de327b7cd30f56da4e824023a80e5 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 18 Nov 2022 14:33:54 +0100 Subject: [PATCH 217/275] Fixed some on member join stuff --- kdb-bot/src/bot/module_list.py | 4 ++-- .../events/level_on_member_join_event.py | 23 +++++++++++++++++++ kdb-bot/src/modules/level/level_module.py | 2 ++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 kdb-bot/src/modules/level/events/level_on_member_join_event.py diff --git a/kdb-bot/src/bot/module_list.py b/kdb-bot/src/bot/module_list.py index cde01cee..ba6be127 100644 --- a/kdb-bot/src/bot/module_list.py +++ b/kdb-bot/src/bot/module_list.py @@ -22,11 +22,11 @@ class ModuleList: return List(type, [ CoreModule, # has to be first! DataModule, + PermissionModule, + DatabaseModule, AutoRoleModule, BaseModule, - DatabaseModule, LevelModule, - PermissionModule, ApiModule, StatsModule, TechnicianModule, diff --git a/kdb-bot/src/modules/level/events/level_on_member_join_event.py b/kdb-bot/src/modules/level/events/level_on_member_join_event.py new file mode 100644 index 00000000..c8ce80cc --- /dev/null +++ b/kdb-bot/src/modules/level/events/level_on_member_join_event.py @@ -0,0 +1,23 @@ +import discord +from cpl_discord.events import OnMemberJoinABC + +from bot_core.helper.event_checks import EventChecks +from bot_core.logging.message_logger import MessageLogger +from modules.level.service.level_service import LevelService + + +class LevelOnMemberJoinEvent(OnMemberJoinABC): + + def __init__( + self, + logger: MessageLogger, + level: LevelService + ): + OnMemberJoinABC.__init__(self) + self._logger = logger + self._level = level + + @EventChecks.check_is_ready() + async def on_member_join(self, member: discord.Member): + self._logger.debug(__name__, f'Module {type(self)} started') + await self._level.check_level(member) diff --git a/kdb-bot/src/modules/level/level_module.py b/kdb-bot/src/modules/level/level_module.py index aeb57409..3502abad 100644 --- a/kdb-bot/src/modules/level/level_module.py +++ b/kdb-bot/src/modules/level/level_module.py @@ -9,6 +9,7 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC from bot_core.abc.module_abc import ModuleABC from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from modules.level.command.level_group import LevelGroup +from modules.level.events.level_on_member_join_event import LevelOnMemberJoinEvent from modules.level.events.level_on_message_event import LevelOnMessageEvent from modules.level.events.level_on_voice_state_update_event import LevelOnVoiceStateUpdateEvent from modules.level.level_seeder import LevelSeeder @@ -36,3 +37,4 @@ class LevelModule(ModuleABC): # events self._dc.add_event(DiscordEventTypesEnum.on_message.value, LevelOnMessageEvent) self._dc.add_event(DiscordEventTypesEnum.on_voice_state_update.value, LevelOnVoiceStateUpdateEvent) + self._dc.add_event(DiscordEventTypesEnum.on_member_join.value, LevelOnMemberJoinEvent) From f136d6164e5be4cdb8b524dfba8860de078ac092 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 18 Nov 2022 15:05:39 +0100 Subject: [PATCH 218/275] Fixed log command --- kdb-bot/src/modules/technician/command/log_command.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/kdb-bot/src/modules/technician/command/log_command.py b/kdb-bot/src/modules/technician/command/log_command.py index 8d8c7832..35b8750f 100644 --- a/kdb-bot/src/modules/technician/command/log_command.py +++ b/kdb-bot/src/modules/technician/command/log_command.py @@ -44,7 +44,7 @@ class LogCommand(DiscordCommandABC): self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') def _reduce_path(self, p: str) -> str: - if p.count('/') == 1 or p == '': + if p.startswith('/') and not p.endswith('/') or p.endswith('/') or p == '': return p return self._reduce_path(os.path.dirname(p)) @@ -80,6 +80,9 @@ class LogCommand(DiscordCommandABC): continue possible_log_paths.append(path) + files_str = "\n\t".join(possible_log_paths.to_list()) + self._logger.debug(__name__, f'Possible log files: \n\t{files_str}') + files = List(str) for possible_path in possible_log_paths: for r, d, f in os.walk(possible_path): @@ -93,6 +96,9 @@ class LogCommand(DiscordCommandABC): files.append(os.path.join(r, file)) + files_str = "\n\t".join(files.to_list()) + self._logger.debug(__name__, f'Log files: \n\t{files_str}') + zip_file = ZipFile('logs.zip', 'w') files.for_each(lambda x: zip_file.write(x)) zip_file.close() From 6e6157ccf24f38bc3ce69ddb0876c2306752a588 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 18 Nov 2022 15:05:39 +0100 Subject: [PATCH 219/275] Fixed log command --- .../modules/technician/command/log_command.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/kdb-bot/src/modules/technician/command/log_command.py b/kdb-bot/src/modules/technician/command/log_command.py index 8d8c7832..13160538 100644 --- a/kdb-bot/src/modules/technician/command/log_command.py +++ b/kdb-bot/src/modules/technician/command/log_command.py @@ -1,10 +1,12 @@ import os -from datetime import datetime +from string import Template from zipfile import ZipFile import discord from cpl_core.dependency_injection import ServiceProviderABC +from cpl_core.environment import ApplicationEnvironmentABC from cpl_core.logging import LoggingSettings +from cpl_core.time import TimeFormatSettings from cpl_discord.command import DiscordCommandABC from cpl_query.extension import List from cpl_translation import TranslatePipe @@ -30,6 +32,8 @@ class LogCommand(DiscordCommandABC): client_utils: ClientUtilsServiceABC, translate: TranslatePipe, permissions: PermissionServiceABC, + time_format: TimeFormatSettings, + env: ApplicationEnvironmentABC ): DiscordCommandABC.__init__(self) @@ -41,10 +45,14 @@ class LogCommand(DiscordCommandABC): self._t = translate self._permissions = permissions + self._env = env + self._log_settings: LoggingSettings = logging_settings + self._time_format_settings: TimeFormatSettings = time_format + self._logger.trace(__name__, f'Loaded command service: {type(self).__name__}') def _reduce_path(self, p: str) -> str: - if p.count('/') == 1 or p == '': + if len(p.split('/')) == 1 or p == '': return p return self._reduce_path(os.path.dirname(p)) @@ -80,6 +88,9 @@ class LogCommand(DiscordCommandABC): continue possible_log_paths.append(path) + files_str = "\n\t".join(possible_log_paths.to_list()) + self._logger.debug(__name__, f'Possible log files: \n\t{files_str}') + files = List(str) for possible_path in possible_log_paths: for r, d, f in os.walk(possible_path): @@ -93,6 +104,9 @@ class LogCommand(DiscordCommandABC): files.append(os.path.join(r, file)) + files_str = "\n\t".join(files.to_list()) + self._logger.debug(__name__, f'Log files: \n\t{files_str}') + zip_file = ZipFile('logs.zip', 'w') files.for_each(lambda x: zip_file.write(x)) zip_file.close() From c5b5297058aa35463db72411b91321c724cc0108 Mon Sep 17 00:00:00 2001 From: Nick Jungmann Date: Sun, 20 Nov 2022 06:21:51 +0100 Subject: [PATCH 220/275] Added presence command #18 --- kdb-bot/src/bot/config | 2 +- kdb-bot/src/bot/translation/de.json | 5 ++ kdb-bot/src/modules/base/base_module.py | 2 + .../modules/base/command/presence_command.py | 46 +++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 kdb-bot/src/modules/base/command/presence_command.py diff --git a/kdb-bot/src/bot/config b/kdb-bot/src/bot/config index 43ae9106..52d19fab 160000 --- a/kdb-bot/src/bot/config +++ b/kdb-bot/src/bot/config @@ -1 +1 @@ -Subproject commit 43ae9106e2032d54cc33de3521fdb08742ab4ecd +Subproject commit 52d19fab6dcfd2e7bce068f01d0e15ddf7d43212 diff --git a/kdb-bot/src/bot/translation/de.json b/kdb-bot/src/bot/translation/de.json index 9398972e..3ab63d0b 100644 --- a/kdb-bot/src/bot/translation/de.json +++ b/kdb-bot/src/bot/translation/de.json @@ -151,6 +151,11 @@ }, "footer": "" }, + "presence": { + "changed": "Presence wurde geändert.", + "removed": "Presence wurde entfernt.", + "max_char_count_exceeded": "Der Text darf nicht mehr als 128 Zeichen lang sein!" + }, "user_info": { "fields": { "id": "Id", diff --git a/kdb-bot/src/modules/base/base_module.py b/kdb-bot/src/modules/base/base_module.py index 0282a180..f2dadd90 100644 --- a/kdb-bot/src/modules/base/base_module.py +++ b/kdb-bot/src/modules/base/base_module.py @@ -11,6 +11,7 @@ from modules.base.command.afk_command import AFKCommand from modules.base.command.help_command import HelpCommand from modules.base.command.info_command import InfoCommand from modules.base.command.ping_command import PingCommand +from modules.base.command.presence_command import PresenceCommand from modules.base.command.purge_command import PurgeCommand from modules.base.command.user_group import UserGroup from modules.base.events.base_on_command_error_event import BaseOnCommandErrorEvent @@ -42,6 +43,7 @@ class BaseModule(ModuleABC): self._dc.add_command(HelpCommand) self._dc.add_command(InfoCommand) self._dc.add_command(PingCommand) + self._dc.add_command(PresenceCommand) self._dc.add_command(PurgeCommand) self._dc.add_command(UserGroup) diff --git a/kdb-bot/src/modules/base/command/presence_command.py b/kdb-bot/src/modules/base/command/presence_command.py new file mode 100644 index 00000000..a453e7e1 --- /dev/null +++ b/kdb-bot/src/modules/base/command/presence_command.py @@ -0,0 +1,46 @@ +import discord +from cpl_discord.command import DiscordCommandABC +from cpl_discord.service import DiscordBotServiceABC +from cpl_translation import TranslatePipe +from discord.ext import commands +from discord.ext.commands import Context + +from bot_core.abc.message_service_abc import MessageServiceABC +from bot_core.helper.command_checks import CommandChecks +from bot_core.logging.command_logger import CommandLogger + + +class PresenceCommand(DiscordCommandABC): + + def __init__( + self, + logger: CommandLogger, + message_service: MessageServiceABC, + bot: DiscordBotServiceABC, + translate: TranslatePipe, + ): + DiscordCommandABC.__init__(self) + + self._logger = logger + self._message_service = message_service + self._bot = bot + self._t = translate + + @commands.hybrid_command() + @commands.guild_only() + @CommandChecks.check_is_ready() + @CommandChecks.check_is_member_technician() + async def presence(self, ctx: Context, text: str = ''): + self._logger.debug(__name__, f'Received command presence {ctx}') + + if text == '': + await self._bot.change_presence(activity=None) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.presence.removed')) + else: + if len(text) > 128: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.presence.max_char_count_exceeded')) + else: + await self._bot.change_presence(activity=discord.Game(name=text)) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.presence.changed')) + + self._logger.trace(__name__, f'Finished presence command') From 7fb6d22c3f11789fea81fe914da0fae7afe6c004 Mon Sep 17 00:00:00 2001 From: Nick Jungmann Date: Sun, 20 Nov 2022 15:38:54 +0100 Subject: [PATCH 221/275] Added requested changes to presence command #18 --- .../src/modules/base/command/presence_command.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/kdb-bot/src/modules/base/command/presence_command.py b/kdb-bot/src/modules/base/command/presence_command.py index a453e7e1..99db2fca 100644 --- a/kdb-bot/src/modules/base/command/presence_command.py +++ b/kdb-bot/src/modules/base/command/presence_command.py @@ -29,18 +29,20 @@ class PresenceCommand(DiscordCommandABC): @commands.hybrid_command() @commands.guild_only() @CommandChecks.check_is_ready() - @CommandChecks.check_is_member_technician() + @CommandChecks.check_is_member_moderator() async def presence(self, ctx: Context, text: str = ''): self._logger.debug(__name__, f'Received command presence {ctx}') if text == '': await self._bot.change_presence(activity=None) await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.presence.removed')) - else: - if len(text) > 128: - await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.presence.max_char_count_exceeded')) - else: - await self._bot.change_presence(activity=discord.Game(name=text)) - await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.presence.changed')) + return + + if len(text) > 128: + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.presence.max_char_count_exceeded')) + return + + await self._bot.change_presence(activity=discord.Game(name=text)) + await self._message_service.send_ctx_msg(ctx, self._t.transform('modules.base.presence.changed')) self._logger.trace(__name__, f'Finished presence command') From bd94c42eae708b553aeb80dcbe136a28748c9d37 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 20 Nov 2022 15:53:04 +0100 Subject: [PATCH 222/275] Added discord login & removed discord register #128 --- kdb-bot/src/bot_api/abc/auth_service_abc.py | 3 + .../controller/auth_discord_controller.py | 20 ++- kdb-bot/src/bot_api/service/auth_service.py | 18 +++ kdb-web/package-lock.json | 134 ++++-------------- kdb-web/package.json | 6 +- .../components/login/login.component.html | 103 +++++++------- .../auth/components/login/login.component.ts | 95 +++++++++---- .../registration/registration.component.html | 6 - .../registration/registration.component.ts | 106 +------------- kdb-web/src/app/services/auth/auth.service.ts | 9 ++ kdb-web/src/assets/config.json | 2 +- kdb-web/src/assets/i18n/de.json | 1 + kdb-web/src/styles.scss | 2 +- 13 files changed, 210 insertions(+), 295 deletions(-) diff --git a/kdb-bot/src/bot_api/abc/auth_service_abc.py b/kdb-bot/src/bot_api/abc/auth_service_abc.py index 72cbbce1..a444cbc4 100644 --- a/kdb-bot/src/bot_api/abc/auth_service_abc.py +++ b/kdb-bot/src/bot_api/abc/auth_service_abc.py @@ -70,6 +70,9 @@ class AuthServiceABC(ABC): @abstractmethod async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO: pass + @abstractmethod + async def login_discord_async(self, oauth_dto: AuthUserDTO) -> TokenDTO: pass + @abstractmethod async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO: pass diff --git a/kdb-bot/src/bot_api/controller/auth_discord_controller.py b/kdb-bot/src/bot_api/controller/auth_discord_controller.py index 78181314..900552b8 100644 --- a/kdb-bot/src/bot_api/controller/auth_discord_controller.py +++ b/kdb-bot/src/bot_api/controller/auth_discord_controller.py @@ -82,8 +82,18 @@ class AuthDiscordController: ), response['id']) return jsonify(result.to_dict()) - @Route.post(f'{BasePath}/register') - async def discord_register(self): - dto: OAuthDTO = JSONProcessor.process(OAuthDTO, request.get_json(force=True, silent=True)) - await self._auth_service.add_auth_user_by_oauth_async(dto) - return '', 200 + @Route.get(f'{BasePath}/login') + async def discord_login(self) -> Response: + response = self._get_user_from_discord_response() + dto = AuthUserDTO( + 0, + response['username'], + response['discriminator'], + response['email'], + str(uuid.uuid4()), + None, + AuthRoleEnum.normal + ) + + result = await self._auth_service.login_discord_async(dto) + return jsonify(result.to_dict()) diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index be0859db..7903c8b4 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -460,6 +460,24 @@ class AuthService(AuthServiceABC): self._db.save_changes() return TokenDTO(token, refresh_token) + async def login_discord_async(self, user_dto: AuthUserDTO) -> TokenDTO: + if user_dto is None: + raise ServiceException(ServiceErrorCode.InvalidData, 'User not set') + + db_user = self._auth_users.find_auth_user_by_email(user_dto.email) + if db_user is None: + await self.add_auth_user_async(user_dto) + # raise ServiceException(ServiceErrorCode.InvalidUser, f'User not found') + + db_user = self._auth_users.get_auth_user_by_email(user_dto.email) + token = self.generate_token(db_user) + refresh_token = self._create_and_save_refresh_token(db_user) + if db_user.forgot_password_id is not None: + db_user.forgot_password_id = None + + self._db.save_changes() + return TokenDTO(token, refresh_token) + async def refresh_async(self, token_dto: TokenDTO) -> TokenDTO: if token_dto is None: raise ServiceException(ServiceErrorCode.InvalidData, f'Token not set') diff --git a/kdb-web/package-lock.json b/kdb-web/package-lock.json index b1efc971..df66cf23 100644 --- a/kdb-web/package-lock.json +++ b/kdb-web/package-lock.json @@ -1,12 +1,12 @@ { "name": "kdb-web", - "version": "0.3.dev70", + "version": "0.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "kdb-web", - "version": "0.3.dev70", + "version": "0.3.0", "dependencies": { "@angular/animations": "^14.0.0", "@angular/common": "^14.0.0", @@ -25,7 +25,6 @@ "primeng": "^14.1.2", "rxjs": "~7.5.0", "socket.io-client": "^4.5.3", - "tslib": "^2.3.0", "zone.js": "~0.11.4" }, "devDependencies": { @@ -33,14 +32,14 @@ "@angular/cli": "~14.0.0", "@angular/compiler-cli": "^14.0.0", "@types/jasmine": "~4.0.0", - "@types/node": "^18.8.3", + "@types/node": "^18.11.9", "jasmine-core": "~4.1.0", "karma": "~6.3.0", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.2.0", "karma-jasmine": "~5.0.0", "karma-jasmine-html-reporter": "~1.7.0", - "ts-node": "~8.3.0", + "tslib": "^2.4.1", "typescript": "~4.7.2" } }, @@ -222,6 +221,12 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, "node_modules/@angular-devkit/build-webpack": { "version": "0.1402.6", "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.6.tgz", @@ -3200,9 +3205,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.11.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.0.tgz", - "integrity": "sha512-IOXCvVRToe7e0ny7HpT/X9Rb2RYtElG1a+VshjwT00HxrM2dWBApHQoqsI6WiY7Q03vdf2bCrIGzVrkF/5t10w==", + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", "dev": true }, "node_modules/@types/parse-json": { @@ -3705,12 +3710,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -4982,15 +4981,6 @@ "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", "dev": true }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -7881,12 +7871,6 @@ "semver": "bin/semver.js" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "node_modules/make-fetch-happen": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", @@ -11730,32 +11714,10 @@ "tree-kill": "cli.js" } }, - "node_modules/ts-node": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", - "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", - "dev": true, - "dependencies": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "^3.0.0" - }, - "bin": { - "ts-node": "dist/bin.js" - }, - "engines": { - "node": ">=4.2.0" - }, - "peerDependencies": { - "typescript": ">=2.0" - } - }, "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, "node_modules/type-fest": { "version": "0.21.3", @@ -12510,15 +12472,6 @@ "node": ">=12" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/zone.js": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz", @@ -12659,6 +12612,12 @@ "dev": true } } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true } } }, @@ -14719,9 +14678,9 @@ "dev": true }, "@types/node": { - "version": "18.11.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.0.tgz", - "integrity": "sha512-IOXCvVRToe7e0ny7HpT/X9Rb2RYtElG1a+VshjwT00HxrM2dWBApHQoqsI6WiY7Q03vdf2bCrIGzVrkF/5t10w==", + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", "dev": true }, "@types/parse-json": { @@ -15153,12 +15112,6 @@ "readable-stream": "^3.6.0" } }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -16095,12 +16048,6 @@ "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", "dev": true }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -18187,12 +18134,6 @@ } } }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "make-fetch-happen": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", @@ -20975,23 +20916,10 @@ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, - "ts-node": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", - "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", - "dev": true, - "requires": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "^3.0.0" - } - }, "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, "type-fest": { "version": "0.21.3", @@ -21518,12 +21446,6 @@ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, "zone.js": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz", diff --git a/kdb-web/package.json b/kdb-web/package.json index c6c72b80..6fe001a6 100644 --- a/kdb-web/package.json +++ b/kdb-web/package.json @@ -1,6 +1,6 @@ { "name": "kdb-web", - "version": "0.3.0", + "version": "0.3.dev128", "scripts": { "ng": "ng", "update-version": "ts-node-esm update-version.ts", @@ -33,7 +33,6 @@ "primeng": "^14.1.2", "rxjs": "~7.5.0", "socket.io-client": "^4.5.3", - "tslib": "^2.3.0", "zone.js": "~0.11.4" }, "devDependencies": { @@ -41,13 +40,14 @@ "@angular/cli": "~14.0.0", "@angular/compiler-cli": "^14.0.0", "@types/jasmine": "~4.0.0", - "@types/node": "^18.8.3", + "@types/node": "^18.11.9", "jasmine-core": "~4.1.0", "karma": "~6.3.0", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.2.0", "karma-jasmine": "~5.0.0", "karma-jasmine-html-reporter": "~1.7.0", + "tslib": "^2.4.1", "typescript": "~4.7.2" } } \ No newline at end of file diff --git a/kdb-web/src/app/modules/auth/components/login/login.component.html b/kdb-web/src/app/modules/auth/components/login/login.component.html index d6ee0326..d26ca6d4 100644 --- a/kdb-web/src/app/modules/auth/components/login/login.component.html +++ b/kdb-web/src/app/modules/auth/components/login/login.component.html @@ -1,60 +1,65 @@ \ No newline at end of file + + diff --git a/kdb-web/src/app/modules/auth/components/login/login.component.ts b/kdb-web/src/app/modules/auth/components/login/login.component.ts index 4a073c9a..8ab34cf9 100644 --- a/kdb-web/src/app/modules/auth/components/login/login.component.ts +++ b/kdb-web/src/app/modules/auth/components/login/login.component.ts @@ -1,20 +1,21 @@ -import { Component, OnInit } from '@angular/core'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; -import { AuthService } from 'src/app/services/auth/auth.service'; -import { AuthUserDTO } from 'src/app/models/auth/auth-user.dto'; -import { Router } from '@angular/router'; -import { catchError } from 'rxjs/operators'; -import { ErrorDTO } from 'src/app/models/error/error-dto'; -import { AuthErrorMessages } from 'src/app/models/auth/auth-error-messages.enum'; -import { ServiceErrorCode } from 'src/app/models/error/service-error-code.enum'; -import { AuthUserAtrErrors } from 'src/app/models/auth/auth-user-atr-errors'; -import { SpinnerService } from 'src/app/services/spinner/spinner.service'; -import { ThemeService } from 'src/app/services/theme/theme.service'; +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms"; +import { AuthService } from "src/app/services/auth/auth.service"; +import { AuthUserDTO } from "src/app/models/auth/auth-user.dto"; +import { ActivatedRoute, Router } from "@angular/router"; +import { catchError } from "rxjs/operators"; +import { ErrorDTO } from "src/app/models/error/error-dto"; +import { AuthErrorMessages } from "src/app/models/auth/auth-error-messages.enum"; +import { ServiceErrorCode } from "src/app/models/error/service-error-code.enum"; +import { AuthUserAtrErrors } from "src/app/models/auth/auth-user-atr-errors"; +import { SpinnerService } from "src/app/services/spinner/spinner.service"; +import { ThemeService } from "src/app/services/theme/theme.service"; +import { throwError } from "rxjs"; @Component({ - selector: 'app-login', - templateUrl: './login.component.html', - styleUrls: ['./login.component.scss'] + selector: "app-login", + templateUrl: "./login.component.html", + styleUrls: ["./login.component.scss"] }) export class LoginComponent implements OnInit { @@ -25,45 +26,83 @@ export class LoginComponent implements OnInit { submitted = false; authUserAtrErrors!: AuthUserAtrErrors; + code!: string; + state!: string; + user!: AuthUserDTO; + oAuthId!: string; constructor( private authService: AuthService, private formBuilder: FormBuilder, private router: Router, private spinnerService: SpinnerService, - private themeService: ThemeService - ) { } + private themeService: ThemeService, + private route: ActivatedRoute + ) { + } ngOnInit(): void { this.spinnerService.showSpinner(); this.authService.isUserLoggedInAsync().then(result => { if (result) { - this.router.navigate(['/dashboard']); + this.router.navigate(["/dashboard"]); + return; } + this.checkDiscordLogin(); this.initLoginForm(); this.resetStateFlags(); this.spinnerService.hideSpinner(); }); } + checkDiscordLogin() { + this.route.queryParams.pipe(catchError(err => { + this.spinnerService.hideSpinner(); + this.router.navigate(["auth", "login"]).then(() => { + }); + return throwError(() => err); + })).subscribe(params => { + if (!params["code"] || !params["state"]) { + this.spinnerService.hideSpinner(); + return; + } + + this.code = params["code"]; + this.state = params["state"]; + this.authService.discordLogin(this.code, this.state).pipe(catchError(err => { + this.spinnerService.hideSpinner(); + this.router.navigate(["auth", "login"]).then(() => { + }); + return throwError(() => err); + })).subscribe(token => { + this.authService.saveToken(token); + this.themeService.loadTheme(); + this.themeService.loadMenu(); + this.spinnerService.hideSpinner(); + this.router.navigate(["/dashboard"]); + }); + } + ); + } + resetStateFlags(): void { this.authUserAtrErrors = new AuthUserAtrErrors(); } initLoginForm(): void { this.loginForm = this.formBuilder.group({ - email: ['', [Validators.required, Validators.email]], - password: ['', [Validators.required, Validators.minLength(8)]] + email: ["", [Validators.required, Validators.email]], + password: ["", [Validators.required, Validators.minLength(8)]] }); } register(): void { - this.router.navigate(['/auth/register']); + this.router.navigate(["/auth/register"]); } forgotPassword(): void { - this.router.navigate(['/auth/forgot-password']); + this.router.navigate(["/auth/forgot-password"]); } login(): void { @@ -76,8 +115,8 @@ export class LoginComponent implements OnInit { this.spinnerService.showSpinner(); const user: AuthUserDTO = { - firstName: '', - lastName: '', + firstName: "", + lastName: "", email: this.loginForm.value.email ?? null, password: this.loginForm.value.password ?? null }; @@ -107,8 +146,14 @@ export class LoginComponent implements OnInit { this.themeService.loadTheme(); this.themeService.loadMenu(); this.spinnerService.hideSpinner(); - this.router.navigate(['/dashboard']); + this.router.navigate(["/dashboard"]); }); } + discordLogin() { + this.authService.getDiscordAuthURL().subscribe(url => { + window.location.href = url.loginUrl; + }); + } + } diff --git a/kdb-web/src/app/modules/auth/components/registration/registration.component.html b/kdb-web/src/app/modules/auth/components/registration/registration.component.html index 3433a400..11a154ef 100644 --- a/kdb-web/src/app/modules/auth/components/registration/registration.component.html +++ b/kdb-web/src/app/modules/auth/components/registration/registration.component.html @@ -72,12 +72,6 @@ [disabled]="loginForm.invalid"> - -