Reviewed-on: sh-edraft.de/kd_discord_bot#71 Reviewed-by: Ebola-Chan <nick.jungmann@gmail.com> Reviewed-by: edraft-dev <dev.sven.heidemann@sh-edraft.de> Closes #70 #72 #75 #83 #85 #86
This commit is contained in:
		| @@ -1,26 +0,0 @@ | ||||
| { | ||||
|   "WorkspaceSettings": { | ||||
|     "DefaultProject": "bot", | ||||
|     "Projects": { | ||||
|       "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", | ||||
|       "moderator": "src/modules/moderator/moderator.json", | ||||
|       "permission": "src/modules/permission/permission.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;" | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -1,64 +0,0 @@ | ||||
| version: "3.9" | ||||
|  | ||||
| volumes: | ||||
|   kdb_prod_1: | ||||
|   kdb_staging_1: | ||||
|   kdb_db_1: | ||||
|   kdb_db_2: | ||||
|  | ||||
| services: | ||||
|   kdb_prod_1: | ||||
|     image: kdb/kdb:0.2.1 | ||||
|     container_name: kdb_prod_1 | ||||
|     depends_on: | ||||
|       - kdb_db_1 | ||||
|     volumes: | ||||
|       - kdb_prod_1:/app | ||||
|     environment: | ||||
|       KDB_ENVIRONMENT: "production" | ||||
|       KDB_TOKEN: "" | ||||
|       KDB_PREFIX: "!k " | ||||
|     restart: 'no' | ||||
|  | ||||
|   kdb_staging_1: | ||||
|     image: kdb/kdb:0.2.1 | ||||
|     container_name: kdb_staging_1 | ||||
|     depends_on: | ||||
|       - kdb_db_1 | ||||
|     volumes: | ||||
|       - kdb_staging_1:/app | ||||
|     environment: | ||||
|       KDB_ENVIRONMENT: "staging" | ||||
|       KDB_TOKEN: "" | ||||
|       KDB_PREFIX: "!kt " | ||||
|     restart: 'no' | ||||
|  | ||||
|   kdb_db_1: | ||||
|     image: mysql:latest | ||||
|     container_name: kdb_db_1 | ||||
|     command: mysqld --default-authentication-plugin=mysql_native_password | ||||
|     restart: unless-stopped | ||||
|     environment: | ||||
|       MYSQL_ROOT_PASSWORD: "kd_kdb" | ||||
|       MYSQL_USER: "kd_kdb" | ||||
|       MYSQL_PASSWORD: "kd_kdb" | ||||
|       MYSQL_DATABASE: "kd_kdb" | ||||
|     ports: | ||||
|       - "3307:3306" | ||||
|     volumes: | ||||
|       - kdb_db_1:/var/lib/mysql | ||||
|  | ||||
|   kdb_db_2: | ||||
|     image: mysql:latest | ||||
|     container_name: kdb_db_2 | ||||
|     command: mysqld --default-authentication-plugin=mysql_native_password | ||||
|     restart: unless-stopped | ||||
|     environment: | ||||
|       MYSQL_ROOT_PASSWORD: "kd_kdb" | ||||
|       MYSQL_USER: "kd_kdb" | ||||
|       MYSQL_PASSWORD: "kd_kdb" | ||||
|       MYSQL_DATABASE: "kd_kdb" | ||||
|     ports: | ||||
|       - "3308:3306" | ||||
|     volumes: | ||||
|       - kdb_db_2:/var/lib/mysql | ||||
							
								
								
									
										18
									
								
								dockerfile
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								dockerfile
									
									
									
									
									
								
							| @@ -1,18 +0,0 @@ | ||||
| # syntax=docker/dockerfile:1 | ||||
| FROM python:3.10.7-bullseye | ||||
|  | ||||
| WORKDIR /app | ||||
| COPY ./dist/bot/build/ . | ||||
|  | ||||
| RUN pip install cpl-core --extra-index-url https://pip.sh-edraft.de | ||||
| RUN pip install cpl-discord --extra-index-url https://pip.sh-edraft.de | ||||
| RUN pip install cpl-query --extra-index-url https://pip.sh-edraft.de | ||||
| RUN pip install cpl-translation --extra-index-url https://pip.sh-edraft.de | ||||
| RUN apt-get update -y | ||||
| RUN apt-get install nano -y | ||||
|  | ||||
| ENV KDB_TOKEN="" | ||||
| ENV KDB_PREFIX="!kdb " | ||||
| ENV KDB_ENVIRONMENT="production" | ||||
|  | ||||
| CMD [ "bash", "/app/bot/bot"] | ||||
							
								
								
									
										43
									
								
								kdb-bot/cpl-workspace.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								kdb-bot/cpl-workspace.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| { | ||||
|   "WorkspaceSettings": { | ||||
|     "DefaultProject": "bot", | ||||
|     "Projects": { | ||||
|       "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", | ||||
|       "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" | ||||
|     }, | ||||
|     "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;" | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										49
									
								
								kdb-bot/docker-compose.dev.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								kdb-bot/docker-compose.dev.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| 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 | ||||
							
								
								
									
										49
									
								
								kdb-bot/docker-compose.staging.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								kdb-bot/docker-compose.staging.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| 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 | ||||
							
								
								
									
										48
									
								
								kdb-bot/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								kdb-bot/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| 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<y" | ||||
|       MYSQL_DATABASE: "kd_kdb" | ||||
|     ports: | ||||
|       - "3307:3306" | ||||
|     volumes: | ||||
|       - kdb_db_prod_1:/var/lib/mysql | ||||
							
								
								
									
										18
									
								
								kdb-bot/dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								kdb-bot/dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| # 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"] | ||||
| @@ -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.dev70' | ||||
| 
 | ||||
| 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='dev70') | ||||
| @@ -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) and self._feature_flags.get_flag(FeatureFlagsEnum.api_only) and self._environment.environment_name == 'development': | ||||
|                 self._api.start() | ||||
|                 self._api.join() | ||||
|                 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  # | ||||
| @@ -14,9 +14,6 @@ elif [[ $1 == "-stage" ]]; then | ||||
| elif [[ $1 == "-prod" ]]; then | ||||
|     export KDB_ENVIRONMENT=production | ||||
|     export KDB_NAME=KDB | ||||
| else | ||||
|     export KDB_ENVIRONMENT=production | ||||
|     export KDB_NAME=KDB-prod | ||||
| fi | ||||
| 
 | ||||
| export PYTHONPATH=./:$PYTHONPATH | ||||
| @@ -3,8 +3,8 @@ | ||||
|     "Name": "bot", | ||||
|     "Version": { | ||||
|       "Major": "0", | ||||
|       "Minor": "2", | ||||
|       "Micro": "3" | ||||
|       "Minor": "3", | ||||
|       "Micro": "dev70" | ||||
|     }, | ||||
|     "Author": "Sven Heidemann", | ||||
|     "AuthorEmail": "sven.heidemann@sh-edraft.de", | ||||
| @@ -16,18 +16,24 @@ | ||||
|     "LicenseName": "MIT", | ||||
|     "LicenseDescription": "MIT, see LICENSE for more details.", | ||||
|     "Dependencies": [ | ||||
|       "cpl-core==2022.10.0.post6", | ||||
|       "cpl-translation==2022.10.0.post1", | ||||
|       "cpl-query==2022.10.0", | ||||
|       "cpl-discord==2022.10.0.post5" | ||||
|       "cpl-core==2022.10.0.post7", | ||||
|       "cpl-translation==2022.10.0.post2", | ||||
|       "cpl-query==2022.10.0.post2", | ||||
|       "cpl-discord==2022.10.0.post6", | ||||
|       "Flask==2.2.2", | ||||
|       "Flask-Classful==0.14.2", | ||||
|       "Flask-Cors==3.0.10", | ||||
|       "PyJWT==2.6.0", | ||||
|       "waitress==2.1.2", | ||||
|       "Flask-SocketIO==5.3.1", | ||||
|       "eventlet==0.33.1", | ||||
|       "requests-oauthlib==1.3.1" | ||||
|     ], | ||||
|     "DevDependencies": [ | ||||
|       "cpl-cli==2022.10.0" | ||||
|     ], | ||||
|     "PythonVersion": ">=3.10.4", | ||||
|     "PythonPath": { | ||||
|       "linux": "" | ||||
|     }, | ||||
|     "PythonPath": {}, | ||||
|     "Classifiers": [] | ||||
|   }, | ||||
|   "BuildSettings": { | ||||
| @@ -45,6 +51,7 @@ | ||||
|     ], | ||||
|     "PackageData": {}, | ||||
|     "ProjectReferences": [ | ||||
|       "../bot_api/bot-api.json", | ||||
|       "../bot_core/bot-core.json", | ||||
|       "../bot_data/bot-data.json", | ||||
|       "../modules/base/base.json", | ||||
| @@ -6,44 +6,61 @@ | ||||
|     "DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S" | ||||
|   }, | ||||
|   "LoggingSettings": { | ||||
|     "Path": "logs/", | ||||
|     "Path": "logs/$date_now/", | ||||
|     "Filename": "bot.log", | ||||
|     "ConsoleLogLevel": "DEBUG", | ||||
|     "FileLogLevel": "DEBUG" | ||||
|     "ConsoleLogLevel": "TRACE", | ||||
|     "FileLogLevel": "TRACE" | ||||
|   }, | ||||
|   "BotLoggingSettings": { | ||||
|     "Api": { | ||||
|       "Path": "logs/$date_now/", | ||||
|       "Filename": "api.log", | ||||
|       "ConsoleLogLevel": "TRACE", | ||||
|       "FileLogLevel": "TRACE" | ||||
|     }, | ||||
|     "Command": { | ||||
|       "Path": "logs/", | ||||
|       "Path": "logs/$date_now/", | ||||
|       "Filename": "commands.log", | ||||
|       "ConsoleLogLevel": "DEBUG", | ||||
|       "FileLogLevel": "DEBUG" | ||||
|       "ConsoleLogLevel": "TRACE", | ||||
|       "FileLogLevel": "TRACE" | ||||
|     }, | ||||
|     "Database": { | ||||
|       "Path": "logs/", | ||||
|       "Path": "logs/$date_now/", | ||||
|       "Filename": "database.log", | ||||
|       "ConsoleLogLevel": "DEBUG", | ||||
|       "FileLogLevel": "DEBUG" | ||||
|       "FileLogLevel": "TRACE" | ||||
|     }, | ||||
|     "Message": { | ||||
|       "Path": "logs/", | ||||
|       "Path": "logs/$date_now/", | ||||
|       "Filename": "message.log", | ||||
|       "ConsoleLogLevel": "DEBUG", | ||||
|       "FileLogLevel": "DEBUG" | ||||
|       "ConsoleLogLevel": "TRACE", | ||||
|       "FileLogLevel": "TRACE" | ||||
|     } | ||||
|   }, | ||||
|   "DiscordBot": { | ||||
|     "Token": "OTk4MTU5NjczODkzMDYwNzM4.GN3QyA.yvWO6L7Eu36gXQ7ARDs0Jg2J1VqIDnHLou5lT4", | ||||
|     "Prefix": "!kd " | ||||
|   }, | ||||
|   "Translation": { | ||||
|     "DefaultLanguage": "de", | ||||
|     "Languages": [ | ||||
|       "de" | ||||
|     ] | ||||
|   }, | ||||
|   "DiscordBot": { | ||||
|     "Token": "OTk4MTU5NjczODkzMDYwNzM4.GN3QyA.yvWO6L7Eu36gXQ7ARDs0Jg2J1VqIDnHLou5lT4", | ||||
|     "Prefix": "!kd " | ||||
|   "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": 6 | ||||
|       "MessageDeleteTimer": 4 | ||||
|     }, | ||||
|     "Technicians": [ | ||||
|       240160344557879316, | ||||
| @@ -12,6 +12,12 @@ | ||||
|     "FileLogLevel": "TRACE" | ||||
|   }, | ||||
|   "BotLoggingSettings": { | ||||
|     "Api": { | ||||
|       "Path": "logs/", | ||||
|       "Filename": "api.log", | ||||
|       "ConsoleLogLevel": "TRACE", | ||||
|       "FileLogLevel": "TRACE" | ||||
|     }, | ||||
|     "Command": { | ||||
|       "Path": "logs/", | ||||
|       "Filename": "commands.log", | ||||
| @@ -12,6 +12,12 @@ | ||||
|     "FileLogLevel": "TRACE" | ||||
|   }, | ||||
|   "BotLoggingSettings": { | ||||
|     "Api": { | ||||
|       "Path": "logs/", | ||||
|       "Filename": "api.log", | ||||
|       "ConsoleLogLevel": "TRACE", | ||||
|       "FileLogLevel": "TRACE" | ||||
|     }, | ||||
|     "Command": { | ||||
|       "Path": "logs/", | ||||
|       "Filename": "commands.log", | ||||
| @@ -12,6 +12,12 @@ | ||||
|     "FileLogLevel": "INFO" | ||||
|   }, | ||||
|   "BotLoggingSettings": { | ||||
|     "Api": { | ||||
|       "Path": "logs/$date_now/", | ||||
|       "Filename": "api.log", | ||||
|       "ConsoleLogLevel": "ERROR", | ||||
|       "FileLogLevel": "INFO" | ||||
|     }, | ||||
|     "Command": { | ||||
|       "Path": "logs/$date_now/", | ||||
|       "Filename": "commands.log", | ||||
| @@ -38,9 +44,9 @@ | ||||
|     ] | ||||
|   }, | ||||
|   "DatabaseSettings": { | ||||
|     "Host": "kdb_db_1", | ||||
|     "Host": "kdb_db_prod_1", | ||||
|     "User": "kd_kdb", | ||||
|     "Password": "a2Rfa2Ri", | ||||
|     "Password": "Kz1nan0ow4RFYlJHNl9TJsO2fcO8PnphTlQ9ckV7X35tPHk=", | ||||
|     "Database": "kd_kdb", | ||||
|     "Port": "3306", | ||||
|     "Charset": "utf8mb4", | ||||
| @@ -12,6 +12,12 @@ | ||||
|     "FileLogLevel": "DEBUG" | ||||
|   }, | ||||
|   "BotLoggingSettings": { | ||||
|     "Api": { | ||||
|       "Path": "logs/$date_now/", | ||||
|       "Filename": "api.log", | ||||
|       "ConsoleLogLevel": "INFO", | ||||
|       "FileLogLevel": "DEBUG" | ||||
|     }, | ||||
|     "Command": { | ||||
|       "Path": "logs/$date_now/", | ||||
|       "Filename": "commands.log", | ||||
| @@ -38,9 +44,9 @@ | ||||
|     ] | ||||
|   }, | ||||
|   "DatabaseSettings": { | ||||
|     "Host": "kdb_db_2", | ||||
|     "Host": "kdb_db_staging_1", | ||||
|     "User": "kd_kdb", | ||||
|     "Password": "a2Rfa2Ri", | ||||
|     "Password": "MyZZd1ZNQ3fDvGI9TFV0N0LCp8OWc1k/S3J+WFJ0RCMmJmY=", | ||||
|     "Database": "kd_kdb", | ||||
|     "Port": "3306", | ||||
|     "Charset": "utf8mb4", | ||||
| @@ -1,5 +1,6 @@ | ||||
| { | ||||
|   "FeatureFlags": { | ||||
|     "ApiModule": true, | ||||
|     "AdminModule": true, | ||||
|     "AutoRoleModule": true, | ||||
|     "BaseModule": true, | ||||
| @@ -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() | ||||
| @@ -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 | ||||
| @@ -26,6 +27,7 @@ class ModuleList: | ||||
|             DatabaseModule, | ||||
|             ModeratorModule, | ||||
|             PermissionModule, | ||||
|             ApiModule, | ||||
|             # has to be last! | ||||
|             BootLogModule, | ||||
|             CoreExtensionModule, | ||||
| @@ -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)) | ||||
| 
 | ||||
| @@ -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 | ||||
| @@ -152,5 +152,26 @@ | ||||
|     "database": {}, | ||||
|     "permission": { | ||||
|     } | ||||
|   }, | ||||
|   "api": { | ||||
|     "mail": { | ||||
|       "automatic_mail": "\n\nDies ist eine automatische E-Mail.\nGesendet von {}-{}@{}" | ||||
|     }, | ||||
|     "api": { | ||||
|       "test_mail": { | ||||
|         "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/register/{}" | ||||
|         }, | ||||
|         "forgot_password": { | ||||
|           "subject": "Passwort für {} {} zurücksetzen", | ||||
|           "message": "Öffne den Link um das Passwort zu ändern:\n{}auth/forgot-password/{}" | ||||
|         } | ||||
|       } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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' | ||||
| __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') | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/abc/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/abc/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.3.dev70' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='3', micro='dev70') | ||||
							
								
								
									
										89
									
								
								kdb-bot/src/bot_api/abc/auth_service_abc.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								kdb-bot/src/bot_api/abc/auth_service_abc.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| 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_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.o_auth_dto import OAuthDTO | ||||
| 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): | ||||
|  | ||||
|     @abstractmethod | ||||
|     def __init__(self): pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     def generate_token(self, user: AuthUser) -> str: pass | ||||
|  | ||||
|     @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 | ||||
|  | ||||
|     @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, with_password: bool = False) -> AuthUserDTO: pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     async def find_auth_user_by_email_async(self, email: str) -> AuthUserDTO: pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     async def add_auth_user_async(self, user_dto: AuthUserDTO): pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     async def add_auth_user_by_oauth_async(self, dto: OAuthDTO): pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     async def add_auth_user_by_discord_async(self, user_dto: AuthUserDTO, dc_id: int) -> OAuthDTO: 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 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 verify_login(self, token_str: str) -> bool: 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 | ||||
|  | ||||
|     @abstractmethod | ||||
|     async def confirm_forgot_password_async(self, id: str) -> EMailStringDTO: pass | ||||
|  | ||||
|     @abstractmethod | ||||
|     async def reset_password_async(self, rp_dto: ResetPasswordDTO): pass | ||||
							
								
								
									
										13
									
								
								kdb-bot/src/bot_api/abc/dto_abc.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								kdb-bot/src/bot_api/abc/dto_abc.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										17
									
								
								kdb-bot/src/bot_api/abc/select_criteria_abc.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								kdb-bot/src/bot_api/abc/select_criteria_abc.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										16
									
								
								kdb-bot/src/bot_api/abc/transformer_abc.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								kdb-bot/src/bot_api/abc/transformer_abc.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| from abc import abstractmethod | ||||
|  | ||||
| from cpl_core.database import TableABC | ||||
|  | ||||
| from bot_api.abc.dto_abc import DtoABC | ||||
|  | ||||
|  | ||||
| class TransformerABC: | ||||
|  | ||||
|     @staticmethod | ||||
|     @abstractmethod | ||||
|     def to_db(dto: DtoABC) -> TableABC: pass | ||||
|  | ||||
|     @staticmethod | ||||
|     @abstractmethod | ||||
|     def to_dto(db: TableABC) -> DtoABC: pass | ||||
							
								
								
									
										156
									
								
								kdb-bot/src/bot_api/api.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								kdb-bot/src/bot_api/api.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | ||||
| import re | ||||
| import sys | ||||
| import textwrap | ||||
| import uuid | ||||
| from functools import partial | ||||
| from typing import Union | ||||
|  | ||||
| import eventlet | ||||
| from cpl_core.dependency_injection import ServiceProviderABC | ||||
| from cpl_core.utils import CredentialManager | ||||
| from eventlet import wsgi | ||||
| from flask import Flask, request, jsonify, Response | ||||
| from flask_cors import CORS | ||||
| from flask_socketio import SocketIO | ||||
| from werkzeug.exceptions import NotFound | ||||
|  | ||||
| from bot_api.configuration.api_settings import ApiSettings | ||||
| 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.logging.api_logger import ApiLogger | ||||
| from bot_api.model.error_dto import ErrorDTO | ||||
| from bot_api.route.route import Route | ||||
|  | ||||
|  | ||||
| class Api(Flask): | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             logger: ApiLogger, | ||||
|             services: ServiceProviderABC, | ||||
|             api_settings: ApiSettings, | ||||
|             frontend_settings: FrontendSettings, | ||||
|             auth_settings: AuthenticationSettings, | ||||
|             *args, **kwargs | ||||
|     ): | ||||
|         if not args: | ||||
|             kwargs.setdefault('import_name', __name__) | ||||
|  | ||||
|         Flask.__init__(self, *args, **kwargs) | ||||
|  | ||||
|         self._logger = logger | ||||
|         self._services = services | ||||
|         self._api_settings = api_settings | ||||
|         self._auth_settings = auth_settings | ||||
|  | ||||
|         self._cors = CORS(self, support_credentials=True) | ||||
|  | ||||
|         # register hooks | ||||
|         self.before_request(self.before_request_hook) | ||||
|         self.after_request(self.after_request_hook) | ||||
|  | ||||
|         # register error handler | ||||
|         exc_class, code = self._get_exc_class_and_code(Exception) | ||||
|         self.register_error_handler(exc_class, self.handle_exception) | ||||
|  | ||||
|         # websockets | ||||
|         self._socketio = SocketIO(self, cors_allowed_origins='*', path='/api/socket.io') | ||||
|         self._socketio.on_event('connect', self.on_connect) | ||||
|         self._socketio.on_event('disconnect', self.on_disconnect) | ||||
|  | ||||
|         self._requests = {} | ||||
|  | ||||
|     @staticmethod | ||||
|     def _get_methods_from_registered_route() -> Union[list[str], str]: | ||||
|         methods = ['Unknown'] | ||||
|         if request.path in Route.registered_routes and len(Route.registered_routes[request.path]) >= 1 and 'methods' in Route.registered_routes[request.path][1]: | ||||
|             methods = Route.registered_routes[request.path][1]['methods'] | ||||
|  | ||||
|         if len(methods) == 1: | ||||
|             return methods[0] | ||||
|         return methods | ||||
|  | ||||
|     def _register_routes(self): | ||||
|         for path, f in Route.registered_routes.items(): | ||||
|             route = f[0] | ||||
|             kwargs = f[1] | ||||
|             cls = None | ||||
|             qual_name_split = route.__qualname__.split('.') | ||||
|             if len(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(route, self if cls is None else cls) | ||||
|             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 | ||||
|         elif isinstance(e, NotFound): | ||||
|             self._logger.error(__name__, e.description) | ||||
|             error = ErrorDTO(ServiceErrorCode.NotFound, e.description) | ||||
|             return jsonify(error.to_dict()), 404 | ||||
|         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_hook(self): | ||||
|         request_id = uuid.uuid4() | ||||
|         self._requests[request] = request_id | ||||
|         method = request.access_control_request_method | ||||
|  | ||||
|         self._logger.info(__name__, f'Received {request_id} @ {self._get_methods_from_registered_route() if method is None else method} {request.url} from {request.remote_addr}') | ||||
|  | ||||
|         headers = str(request.headers).replace('\n', '\n\t\t') | ||||
|         data = request.get_data() | ||||
|         data = '' if len(data) == 0 else str(data.decode(encoding="utf-8")) | ||||
|  | ||||
|         text = textwrap.dedent(f'Request: {request_id}:\n\tHeader:\n\t\t{headers}\n\tUser-Agent: {request.user_agent.string}\n\tBody: {data}') | ||||
|         self._logger.trace(__name__, text) | ||||
|  | ||||
|     def after_request_hook(self, response: Response): | ||||
|         method = request.access_control_request_method | ||||
|         request_id = f'{self._get_methods_from_registered_route() if method is None else method} {request.url} from {request.remote_addr}' | ||||
|         if request in self._requests: | ||||
|             request_id = self._requests[request] | ||||
|  | ||||
|         self._logger.info(__name__, f'Answered {request_id}') | ||||
|  | ||||
|         headers = str(request.headers).replace('\n', '\n\t\t') | ||||
|         data = request.get_data() | ||||
|         data = '' if len(data) == 0 else str(data.decode(encoding="utf-8")) | ||||
|  | ||||
|         text = textwrap.dedent(f'Request: {request_id}:\n\tHeader:\n\t\t{headers}\n\tResponse: {data}') | ||||
|         self._logger.trace(__name__, text) | ||||
|  | ||||
|         return response | ||||
|  | ||||
|     def start(self): | ||||
|         self._logger.info(__name__, f'Starting API {self._api_settings.host}:{self._api_settings.port}') | ||||
|         self._register_routes() | ||||
|         self.secret_key = CredentialManager.decrypt(self._auth_settings.secret_key) | ||||
|         # 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) | ||||
|         wsgi.server( | ||||
|             eventlet.listen((self._api_settings.host, self._api_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') | ||||
							
								
								
									
										52
									
								
								kdb-bot/src/bot_api/api_module.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								kdb-bot/src/bot_api/api_module.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| 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.discord_event_types_enum import DiscordEventTypesEnum | ||||
| 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.auth_controller import AuthController | ||||
| from bot_api.controller.auth_discord_controller import AuthDiscordController | ||||
| from bot_api.controller.discord.server_controller import ServerController | ||||
| from bot_api.controller.gui_controller import GuiController | ||||
| 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 | ||||
|  | ||||
|  | ||||
| class ApiModule(ModuleABC): | ||||
|  | ||||
|     def __init__(self, dc: DiscordCollectionABC): | ||||
|         ModuleABC.__init__(self, dc, FeatureFlagsEnum.api_module) | ||||
|  | ||||
|     def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC): | ||||
|         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) | ||||
|  | ||||
|         services.add_transient(AuthServiceABC, AuthService) | ||||
|         services.add_transient(AuthController) | ||||
|         services.add_transient(AuthDiscordController) | ||||
|         services.add_transient(GuiController) | ||||
|         services.add_transient(DiscordService) | ||||
|         services.add_transient(ServerController) | ||||
|  | ||||
|         # cpl-discord | ||||
|         self._dc.add_event(DiscordEventTypesEnum.on_ready.value, BotApiOnReadyEvent) | ||||
							
								
								
									
										24
									
								
								kdb-bot/src/bot_api/api_thread.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								kdb-bot/src/bot_api/api_thread.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| import threading | ||||
|  | ||||
| from bot_api.api import Api | ||||
| from bot_api.logging.api_logger import ApiLogger | ||||
|  | ||||
|  | ||||
| class ApiThread(threading.Thread): | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             logger: ApiLogger, | ||||
|             api: Api | ||||
|     ): | ||||
|         threading.Thread.__init__(self, daemon=True) | ||||
|  | ||||
|         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) | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/app_api_extension.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/app_api_extension.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||
							
								
								
									
										44
									
								
								kdb-bot/src/bot_api/bot-api.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								kdb-bot/src/bot_api/bot-api.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| { | ||||
|   "ProjectSettings": { | ||||
|     "Name": "bot-api", | ||||
|     "Version": { | ||||
|       "Major": "0", | ||||
|       "Minor": "3", | ||||
|       "Micro": "dev70" | ||||
|     }, | ||||
|     "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.4", | ||||
|     "PythonPath": {}, | ||||
|     "Classifiers": [] | ||||
|   }, | ||||
|   "BuildSettings": { | ||||
|     "ProjectType": "library", | ||||
|     "SourcePath": "", | ||||
|     "OutputPath": "../../dist", | ||||
|     "Main": "bot_api.main", | ||||
|     "EntryPoint": "bot-api", | ||||
|     "IncludePackageData": false, | ||||
|     "Included": [], | ||||
|     "Excluded": [ | ||||
|       "*/__pycache__", | ||||
|       "*/logs", | ||||
|       "*/tests" | ||||
|     ], | ||||
|     "PackageData": {}, | ||||
|     "ProjectReferences": [] | ||||
|   } | ||||
| } | ||||
							
								
								
									
										33
									
								
								kdb-bot/src/bot_api/config/apisettings.development.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								kdb-bot/src/bot_api/config/apisettings.development.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| { | ||||
|   "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==" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										28
									
								
								kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| { | ||||
|   "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/" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										27
									
								
								kdb-bot/src/bot_api/config/apisettings.edrafts-pc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								kdb-bot/src/bot_api/config/apisettings.edrafts-pc.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| { | ||||
|   "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/" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										1
									
								
								kdb-bot/src/bot_api/config/apisettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								kdb-bot/src/bot_api/config/apisettings.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {} | ||||
							
								
								
									
										33
									
								
								kdb-bot/src/bot_api/config/apisettings.production.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								kdb-bot/src/bot_api/config/apisettings.production.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| { | ||||
|   "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" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										33
									
								
								kdb-bot/src/bot_api/config/apisettings.staging.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								kdb-bot/src/bot_api/config/apisettings.staging.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| { | ||||
|   "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" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										1
									
								
								kdb-bot/src/bot_api/config/appsettings.PC-Nick.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								kdb-bot/src/bot_api/config/appsettings.PC-Nick.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| {} | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/configuration/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/configuration/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.configuration' | ||||
| __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') | ||||
							
								
								
									
										35
									
								
								kdb-bot/src/bot_api/configuration/api_settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								kdb-bot/src/bot_api/configuration/api_settings.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| 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._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') | ||||
|             Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}') | ||||
							
								
								
									
										48
									
								
								kdb-bot/src/bot_api/configuration/authentication_settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								kdb-bot/src/bot_api/configuration/authentication_settings.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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()}') | ||||
| @@ -0,0 +1,48 @@ | ||||
| import traceback | ||||
|  | ||||
| from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC | ||||
| from cpl_core.console import Console | ||||
| from cpl_query.extension import List | ||||
|  | ||||
|  | ||||
| class DiscordAuthenticationSettings(ConfigurationModelABC): | ||||
|  | ||||
|     def __init__(self): | ||||
|         ConfigurationModelABC.__init__(self) | ||||
|  | ||||
|         self._client_secret = '' | ||||
|         self._redirect_url = '' | ||||
|         self._scope = List() | ||||
|         self._token_url = '' | ||||
|         self._auth_url = '' | ||||
|  | ||||
|     @property | ||||
|     def client_secret(self) -> str: | ||||
|         return self._client_secret | ||||
|  | ||||
|     @property | ||||
|     def redirect_url(self) -> str: | ||||
|         return self._redirect_url | ||||
|  | ||||
|     @property | ||||
|     def scope(self) -> List[str]: | ||||
|         return self._scope | ||||
|  | ||||
|     @property | ||||
|     def token_url(self) -> str: | ||||
|         return self._token_url | ||||
|  | ||||
|     @property | ||||
|     def auth_url(self) -> str: | ||||
|         return self._auth_url | ||||
|  | ||||
|     def from_dict(self, settings: dict): | ||||
|         try: | ||||
|             self._client_secret = settings['ClientSecret'] | ||||
|             self._redirect_url = settings['RedirectURL'] | ||||
|             self._scope = List(str, settings['Scope']) | ||||
|             self._token_url = settings['TokenURL'] | ||||
|             self._auth_url = settings['AuthURL'] | ||||
|         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()}') | ||||
							
								
								
									
										23
									
								
								kdb-bot/src/bot_api/configuration/frontend_settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								kdb-bot/src/bot_api/configuration/frontend_settings.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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()}') | ||||
							
								
								
									
										55
									
								
								kdb-bot/src/bot_api/configuration/version_settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								kdb-bot/src/bot_api/configuration/version_settings.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/controller/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/controller/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.3.dev70' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='3', micro='dev70') | ||||
							
								
								
									
										155
									
								
								kdb-bot/src/bot_api/controller/auth_controller.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								kdb-bot/src/bot_api/controller/auth_controller.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,155 @@ | ||||
| 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 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 | ||||
| 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 | ||||
| from bot_data.model.auth_role_enum import AuthRoleEnum | ||||
|  | ||||
|  | ||||
| class AuthController: | ||||
|     BasePath = '/api/auth' | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             config: ConfigurationABC, | ||||
|             env: ApplicationEnvironmentABC, | ||||
|             logger: ApiLogger, | ||||
|             t: TranslatePipe, | ||||
|             api: Api, | ||||
|             mail_settings: EMailClientSettings, | ||||
|             mailer: EMailClientABC, | ||||
|             auth_service: AuthServiceABC | ||||
|     ): | ||||
|         self._config = config | ||||
|         self._env = env | ||||
|         self._logger = logger | ||||
|         self._t = t | ||||
|         self._api = api | ||||
|         self._mail_settings = mail_settings | ||||
|         self._mailer = mailer | ||||
|         self._auth_service = auth_service | ||||
|  | ||||
|     @Route.get(f'{BasePath}/users') | ||||
|     @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(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) | ||||
|         result.result = result.result.select(lambda x: x.to_dict()) | ||||
|         return jsonify(result.to_dict()) | ||||
|  | ||||
|     @Route.get(f'{BasePath}/users/get/<email>') | ||||
|     @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/<email>') | ||||
|     @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()) | ||||
|  | ||||
|     @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}/register-by-id/<id>') | ||||
|     async def register_id(self, id: str): | ||||
|         result = await self._auth_service.confirm_email_async(id) | ||||
|         return jsonify(result) | ||||
|  | ||||
|     @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.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/<email>') | ||||
|     async def forgot_password(self, email: str): | ||||
|         await self._auth_service.forgot_password_async(email) | ||||
|         return '', 200 | ||||
|  | ||||
|     @Route.post(f'{BasePath}/confirm-forgot-password/<id>') | ||||
|     async def confirm_forgot_password(self, id: str): | ||||
|         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') | ||||
|     @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(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) | ||||
|         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') | ||||
|     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(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/<email>') | ||||
|     @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 | ||||
							
								
								
									
										89
									
								
								kdb-bot/src/bot_api/controller/auth_discord_controller.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								kdb-bot/src/bot_api/controller/auth_discord_controller.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| import os | ||||
| import uuid | ||||
|  | ||||
| from cpl_core.configuration import ConfigurationABC | ||||
| from cpl_core.environment import ApplicationEnvironmentABC | ||||
| from cpl_core.mailing import EMailClientABC, EMailClientSettings | ||||
| from cpl_core.utils import CredentialManager | ||||
| from cpl_discord.service import DiscordBotServiceABC | ||||
| from cpl_translation import TranslatePipe | ||||
| from flask import jsonify, Response | ||||
| from flask import request, session | ||||
| from requests_oauthlib import OAuth2Session | ||||
|  | ||||
| from bot_api.abc.auth_service_abc import AuthServiceABC | ||||
| from bot_api.api import Api | ||||
| from bot_api.configuration.discord_authentication_settings import DiscordAuthenticationSettings | ||||
| from bot_api.json_processor import JSONProcessor | ||||
| from bot_api.logging.api_logger import ApiLogger | ||||
| from bot_api.model.auth_user_dto import AuthUserDTO | ||||
| from bot_api.model.o_auth_dto import OAuthDTO | ||||
| from bot_api.route.route import Route | ||||
| from bot_data.model.auth_role_enum import AuthRoleEnum | ||||
|  | ||||
| # Disable SSL requirement | ||||
| os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' | ||||
|  | ||||
|  | ||||
| class AuthDiscordController: | ||||
|     BasePath = '/api/auth/discord' | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             auth_settings: DiscordAuthenticationSettings, | ||||
|             config: ConfigurationABC, | ||||
|             env: ApplicationEnvironmentABC, | ||||
|             logger: ApiLogger, | ||||
|             bot: DiscordBotServiceABC, | ||||
|             t: TranslatePipe, | ||||
|             api: Api, | ||||
|             mail_settings: EMailClientSettings, | ||||
|             mailer: EMailClientABC, | ||||
|             auth_service: AuthServiceABC | ||||
|     ): | ||||
|         self._auth_settings = auth_settings | ||||
|         self._config = config | ||||
|         self._env = env | ||||
|         self._logger = logger | ||||
|         self._bot = bot | ||||
|         self._t = t | ||||
|         self._api = api | ||||
|         self._mail_settings = mail_settings | ||||
|         self._mailer = mailer | ||||
|         self._auth_service = auth_service | ||||
|  | ||||
|     def _get_user_from_discord_response(self) -> dict: | ||||
|         discord = OAuth2Session(self._bot.user.id, redirect_uri=self._auth_settings.redirect_url, state=request.args.get('state'), scope=self._auth_settings.scope) | ||||
|         token = discord.fetch_token( | ||||
|             self._auth_settings.token_url, | ||||
|             client_secret=CredentialManager.decrypt(self._auth_settings.client_secret), | ||||
|             authorization_response=request.url, | ||||
|         ) | ||||
|         discord = OAuth2Session(self._bot.user.id, token=token) | ||||
|         return discord.get('https://discordapp.com/api' + '/users/@me').json() | ||||
|  | ||||
|     @Route.get(f'{BasePath}/get-url') | ||||
|     async def get_url(self): | ||||
|         oauth = OAuth2Session(self._bot.user.id, redirect_uri=self._auth_settings.redirect_url, scope=self._auth_settings.scope) | ||||
|         login_url, state = oauth.authorization_url(self._auth_settings.auth_url) | ||||
|         return jsonify({'loginUrl': login_url}) | ||||
|  | ||||
|     @Route.get(f'{BasePath}/create-user') | ||||
|     async def discord_create_user(self) -> Response: | ||||
|         response = self._get_user_from_discord_response() | ||||
|         result = await self._auth_service.add_auth_user_by_discord_async(AuthUserDTO( | ||||
|             0, | ||||
|             response['username'], | ||||
|             response['discriminator'], | ||||
|             response['email'], | ||||
|             str(uuid.uuid4()), | ||||
|             None, | ||||
|             AuthRoleEnum.normal | ||||
|         ), 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 | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/controller/discord/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/controller/discord/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.discord' | ||||
| __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') | ||||
							
								
								
									
										65
									
								
								kdb-bot/src/bot_api/controller/discord/server_controller.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								kdb-bot/src/bot_api/controller/discord/server_controller.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| from cpl_core.configuration import ConfigurationABC | ||||
| from cpl_core.environment import ApplicationEnvironmentABC | ||||
| from cpl_core.mailing import EMailClientABC, EMailClientSettings | ||||
| from cpl_translation import TranslatePipe | ||||
| from flask import Response, jsonify, request | ||||
|  | ||||
| from bot_api.api import Api | ||||
| from bot_api.filter.discord.server_select_criteria import ServerSelectCriteria | ||||
| from bot_api.json_processor import JSONProcessor | ||||
| from bot_api.logging.api_logger import ApiLogger | ||||
| from bot_api.route.route import Route | ||||
| from bot_api.service.discord_service import DiscordService | ||||
| from bot_data.model.auth_role_enum import AuthRoleEnum | ||||
|  | ||||
|  | ||||
| class ServerController: | ||||
|     BasePath = f'/api/discord/server' | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             config: ConfigurationABC, | ||||
|             env: ApplicationEnvironmentABC, | ||||
|             logger: ApiLogger, | ||||
|             t: TranslatePipe, | ||||
|             api: Api, | ||||
|             mail_settings: EMailClientSettings, | ||||
|             mailer: EMailClientABC, | ||||
|             discord_service: DiscordService | ||||
|     ): | ||||
|         self._config = config | ||||
|         self._env = env | ||||
|         self._logger = logger | ||||
|         self._t = t | ||||
|         self._api = api | ||||
|         self._mail_settings = mail_settings | ||||
|         self._mailer = mailer | ||||
|         self._discord_service = discord_service | ||||
|  | ||||
|     @Route.get(f'{BasePath}/get/servers') | ||||
|     @Route.authorize(role=AuthRoleEnum.admin) | ||||
|     async def get_all_servers(self) -> Response: | ||||
|         result = await self._discord_service.get_all_servers() | ||||
|         result = result.select(lambda x: x.to_dict()) | ||||
|         return jsonify(result) | ||||
|  | ||||
|     @Route.get(f'{BasePath}/get/servers-by-user') | ||||
|     @Route.authorize | ||||
|     async def get_all_servers_by_user(self) -> Response: | ||||
|         result = await self._discord_service.get_all_servers_by_user() | ||||
|         result = result.select(lambda x: x.to_dict()) | ||||
|         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()) | ||||
|  | ||||
|     @Route.get(f'{BasePath}/get/<id>') | ||||
|     @Route.authorize | ||||
|     async def get_server_by_id(self, id: int) -> Response: | ||||
|         result = await self._discord_service.get_server_by_id_async(id) | ||||
|         return jsonify(result.to_dict()) | ||||
							
								
								
									
										78
									
								
								kdb-bot/src/bot_api/controller/gui_controller.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								kdb-bot/src/bot_api/controller/gui_controller.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| 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 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 | ||||
| from bot_api.route.route import Route | ||||
|  | ||||
|  | ||||
| class GuiController: | ||||
|     BasePath = f'/api/gui' | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             config: ConfigurationABC, | ||||
|             env: ApplicationEnvironmentABC, | ||||
|             logger: ApiLogger, | ||||
|             t: TranslatePipe, | ||||
|             api: Api, | ||||
|             mail_settings: EMailClientSettings, | ||||
|             mailer: EMailClientABC, | ||||
|             auth_settings: AuthenticationSettings | ||||
|     ): | ||||
|         self._config = config | ||||
|         self._env = env | ||||
|         self._logger = logger | ||||
|         self._t = t | ||||
|         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): | ||||
|         import bot_api | ||||
|         version = bot_api.version_info | ||||
|         return VersionDTO(version.major, version.minor, version.micro).to_dict() | ||||
|  | ||||
|     @Route.get(f'{BasePath}/settings') | ||||
|     @Route.authorize | ||||
|     async def settings(self): | ||||
|         import bot_api | ||||
|         version = bot_api.version_info | ||||
|  | ||||
|         return jsonify(SettingsDTO( | ||||
|             '', | ||||
|             VersionDTO(version.major, version.minor, version.micro), | ||||
|             os.path.abspath(os.path.join(self._env.working_directory, 'config')), | ||||
|             '/', | ||||
|             '/', | ||||
|             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()) | ||||
|  | ||||
|     @Route.post(f'{BasePath}/send-test-mail/<email>') | ||||
|     @Route.authorize | ||||
|     async def send_test_mail(self, email: str): | ||||
|         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) | ||||
|         return '', 200 | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/event/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/event/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.event' | ||||
| __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') | ||||
							
								
								
									
										13
									
								
								kdb-bot/src/bot_api/event/bot_api_on_ready_event.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								kdb-bot/src/bot_api/event/bot_api_on_ready_event.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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() | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/exception/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/exception/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.3.dev70' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='3', micro='dev70') | ||||
							
								
								
									
										24
									
								
								kdb-bot/src/bot_api/exception/service_error_code_enum.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								kdb-bot/src/bot_api/exception/service_error_code_enum.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| from enum import Enum | ||||
|  | ||||
| from werkzeug.exceptions import Unauthorized | ||||
|  | ||||
|  | ||||
| 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 | ||||
|  | ||||
|     Unauthorized = 11 | ||||
|     Forbidden = 12 | ||||
							
								
								
									
										13
									
								
								kdb-bot/src/bot_api/exception/service_exception.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								kdb-bot/src/bot_api/exception/service_exception.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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}' | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/filter/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/filter/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.3.dev70' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='3', micro='dev70') | ||||
							
								
								
									
										23
									
								
								kdb-bot/src/bot_api/filter/auth_user_select_criteria.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								kdb-bot/src/bot_api/filter/auth_user_select_criteria.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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: int | ||||
|     ): | ||||
|         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 | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/filter/discord/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/filter/discord/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.discord' | ||||
| __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') | ||||
							
								
								
									
										17
									
								
								kdb-bot/src/bot_api/filter/discord/server_select_criteria.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								kdb-bot/src/bot_api/filter/discord/server_select_criteria.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										39
									
								
								kdb-bot/src/bot_api/json_processor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								kdb-bot/src/bot_api/json_processor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| 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 = 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: | ||||
|                     value = values[name] | ||||
|                 else: | ||||
|                     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) | ||||
|  | ||||
|             else: | ||||
|                 args.append(None) | ||||
|  | ||||
|         return _t(*args) | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/logging/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/logging/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.3.dev70' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='3', micro='dev70') | ||||
							
								
								
									
										11
									
								
								kdb-bot/src/bot_api/logging/api_logger.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								kdb-bot/src/bot_api/logging/api_logger.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/model/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/model/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.3.dev70' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='3', micro='dev70') | ||||
							
								
								
									
										99
									
								
								kdb-bot/src/bot_api/model/auth_user_dto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								kdb-bot/src/bot_api/model/auth_user_dto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| from typing import Optional | ||||
|  | ||||
| from bot_api.abc.dto_abc import DtoABC | ||||
| from bot_data.model.auth_role_enum import AuthRoleEnum | ||||
|  | ||||
|  | ||||
| class AuthUserDTO(DtoABC): | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             id: int = None, | ||||
|             first_name: str = None, | ||||
|             last_name: str = None, | ||||
|             email: str = None, | ||||
|             password: str = None, | ||||
|             confirmation_id: Optional[str] = None, | ||||
|             auth_role: AuthRoleEnum = None, | ||||
|     ): | ||||
|         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 | ||||
|  | ||||
|     @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) -> 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 | ||||
|  | ||||
|     @auth_role.setter | ||||
|     def auth_role(self, value: AuthRoleEnum): | ||||
|         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'] | ||||
|  | ||||
|     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.value, | ||||
|         } | ||||
							
								
								
									
										21
									
								
								kdb-bot/src/bot_api/model/auth_user_filtered_result_dto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								kdb-bot/src/bot_api/model/auth_user_filtered_result_dto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
|         } | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/model/discord/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/model/discord/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.discord' | ||||
| __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') | ||||
							
								
								
									
										58
									
								
								kdb-bot/src/bot_api/model/discord/server_dto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								kdb-bot/src/bot_api/model/discord/server_dto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| from typing import Optional | ||||
|  | ||||
| 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, | ||||
|             icon_url: Optional[str] | ||||
|  | ||||
|     ): | ||||
|         DtoABC.__init__(self) | ||||
|  | ||||
|         self._server_id = server_id | ||||
|         self._discord_id = discord_id | ||||
|         self._name = name | ||||
|         self._member_count = member_count | ||||
|         self._icon_url = icon_url | ||||
|          | ||||
|     @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 | ||||
|  | ||||
|     @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._icon_url = values['iconURL'] | ||||
|  | ||||
|     def to_dict(self) -> dict: | ||||
|         return { | ||||
|             'serverId': self._server_id, | ||||
|             'discordId': self._discord_id, | ||||
|             'name': self._name, | ||||
|             'memberCount': self._member_count, | ||||
|             'iconURL': self._icon_url, | ||||
|         } | ||||
| @@ -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 | ||||
|         } | ||||
							
								
								
									
										21
									
								
								kdb-bot/src/bot_api/model/email_string_dto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								kdb-bot/src/bot_api/model/email_string_dto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| import traceback | ||||
|  | ||||
| from cpl_core.console import Console | ||||
|  | ||||
| from bot_api.abc.dto_abc import DtoABC | ||||
|  | ||||
|  | ||||
| class EMailStringDTO(DtoABC): | ||||
|  | ||||
|     def __init__(self, email: str): | ||||
|         DtoABC.__init__(self) | ||||
|  | ||||
|         self._email = email | ||||
|  | ||||
|     def from_dict(self, values: dict): | ||||
|         self._email = values['email'] | ||||
|  | ||||
|     def to_dict(self) -> dict: | ||||
|         return { | ||||
|             'email': self._email | ||||
|         } | ||||
							
								
								
									
										34
									
								
								kdb-bot/src/bot_api/model/error_dto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								kdb-bot/src/bot_api/model/error_dto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
|         } | ||||
							
								
								
									
										44
									
								
								kdb-bot/src/bot_api/model/o_auth_dto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								kdb-bot/src/bot_api/model/o_auth_dto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| from typing import Optional | ||||
|  | ||||
| from bot_api.abc.dto_abc import DtoABC | ||||
| from bot_api.model.auth_user_dto import AuthUserDTO | ||||
| from bot_data.model.auth_role_enum import AuthRoleEnum | ||||
|  | ||||
|  | ||||
| class OAuthDTO(DtoABC): | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             user: AuthUserDTO, | ||||
|             o_auth_id: Optional[str], | ||||
|     ): | ||||
|         DtoABC.__init__(self) | ||||
|  | ||||
|         self._user = user | ||||
|         self._oauth_id = o_auth_id | ||||
|  | ||||
|     @property | ||||
|     def user(self) -> AuthUserDTO: | ||||
|         return self._user | ||||
|  | ||||
|     @user.setter | ||||
|     def user(self, value: AuthUserDTO): | ||||
|         self._user = value | ||||
|  | ||||
|     @property | ||||
|     def oauth_id(self) -> Optional[str]: | ||||
|         return self._oauth_id | ||||
|  | ||||
|     @oauth_id.setter | ||||
|     def oauth_id(self, value: Optional[str]): | ||||
|         self._oauth_id = value | ||||
|  | ||||
|     def from_dict(self, values: dict): | ||||
|         self._user = AuthUserDTO().from_dict(values['user']) | ||||
|         self._oauth_id = values['oAuthId'] | ||||
|  | ||||
|     def to_dict(self) -> dict: | ||||
|         return { | ||||
|             'user': self._user.to_dict(), | ||||
|             'oAuthId': self._oauth_id | ||||
|         } | ||||
							
								
								
									
										32
									
								
								kdb-bot/src/bot_api/model/reset_password_dto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								kdb-bot/src/bot_api/model/reset_password_dto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| import traceback | ||||
|  | ||||
| from cpl_core.console import Console | ||||
|  | ||||
| from bot_api.abc.dto_abc import DtoABC | ||||
|  | ||||
|  | ||||
| class ResetPasswordDTO(DtoABC): | ||||
|  | ||||
|     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): | ||||
|         self._id = values['id'] | ||||
|         self._password = values['password'] | ||||
|  | ||||
|     def to_dict(self) -> dict: | ||||
|         return { | ||||
|             'id': self._id, | ||||
|             'password': self._password | ||||
|         } | ||||
							
								
								
									
										67
									
								
								kdb-bot/src/bot_api/model/settings_dto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								kdb-bot/src/bot_api/model/settings_dto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| 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 = 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 = token_expire_time | ||||
|         self._refresh_token_expire_time = refresh_token_expire_time | ||||
|  | ||||
|         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'] | ||||
|         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.str, | ||||
|             '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, | ||||
|         } | ||||
							
								
								
									
										32
									
								
								kdb-bot/src/bot_api/model/token_dto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								kdb-bot/src/bot_api/model/token_dto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| import traceback | ||||
|  | ||||
| from cpl_core.console import Console | ||||
|  | ||||
| from bot_api.abc.dto_abc import DtoABC | ||||
|  | ||||
|  | ||||
| class TokenDTO(DtoABC): | ||||
|  | ||||
|     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): | ||||
|         self._token = values['token'] | ||||
|         self._refresh_token = values['refreshToken'] | ||||
|  | ||||
|     def to_dict(self) -> dict: | ||||
|         return { | ||||
|             'token': self._token, | ||||
|             'refreshToken': self._refresh_token | ||||
|         } | ||||
							
								
								
									
										45
									
								
								kdb-bot/src/bot_api/model/update_auth_user_dto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								kdb-bot/src/bot_api/model/update_auth_user_dto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| 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, | ||||
|             auth_user_dto: AuthUserDTO, | ||||
|             new_auth_user_dto: AuthUserDTO, | ||||
|             change_password: bool = False | ||||
|     ): | ||||
|         DtoABC.__init__(self) | ||||
|  | ||||
|         self._auth_user = auth_user_dto | ||||
|         self._new_auth_user = new_auth_user_dto | ||||
|         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): | ||||
|         self._auth_user = values['authUser'] | ||||
|         self._new_auth_user = values['newAuthUser'] | ||||
|         self._change_password = False if 'changePassword' not in values else bool(values['changePassword']) | ||||
|  | ||||
|     def to_dict(self) -> dict: | ||||
|         return { | ||||
|             'authUser': self._auth_user, | ||||
|             'newAuthUser': self._new_auth_user, | ||||
|             'changePassword': self._change_password | ||||
|         } | ||||
							
								
								
									
										43
									
								
								kdb-bot/src/bot_api/model/version_dto.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								kdb-bot/src/bot_api/model/version_dto.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| 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 | ||||
|  | ||||
|     @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'] | ||||
|         self._micro = values['micro'] | ||||
|  | ||||
|     def to_dict(self) -> dict: | ||||
|         return { | ||||
|             'major': self._major, | ||||
|             'minor': self._minor, | ||||
|             'micro': self._micro, | ||||
|         } | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/route/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/route/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.3.dev70' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='3', micro='dev70') | ||||
							
								
								
									
										103
									
								
								kdb-bot/src/bot_api/route/route.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								kdb-bot/src/bot_api/route/route.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| import functools | ||||
| from functools import wraps | ||||
| from typing import Optional, Callable | ||||
|  | ||||
| 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 | ||||
| from bot_data.model.auth_role_enum import AuthRoleEnum | ||||
|  | ||||
|  | ||||
| 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: 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 | ||||
|             if 'Authorization' in request.headers: | ||||
|                 bearer = request.headers.get('Authorization') | ||||
|                 token = bearer.split()[1] | ||||
|  | ||||
|             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: | ||||
|                 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): | ||||
|                 ex = ServiceException(ServiceErrorCode.Unauthorized, f'Token expired') | ||||
|                 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 | ||||
|  | ||||
|     @classmethod | ||||
|     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 | ||||
|  | ||||
|         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) | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/service/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/service/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.service' | ||||
| __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') | ||||
							
								
								
									
										537
									
								
								kdb-bot/src/bot_api/service/auth_service.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										537
									
								
								kdb-bot/src/bot_api/service/auth_service.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,537 @@ | ||||
| import hashlib | ||||
| import re | ||||
| import textwrap | ||||
| import uuid | ||||
| from datetime import datetime, timedelta, timezone | ||||
| from threading import Thread | ||||
| from typing import Optional | ||||
|  | ||||
| import jwt | ||||
| from cpl_core.database.context import DatabaseContextABC | ||||
| from cpl_core.environment import ApplicationEnvironmentABC | ||||
| from cpl_core.mailing import EMail, EMailClientABC | ||||
| from cpl_core.utils import CredentialManager | ||||
| from cpl_discord.service import DiscordBotServiceABC | ||||
| 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 | ||||
| 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 | ||||
| from bot_api.model.auth_user_filtered_result_dto import AuthUserFilteredResultDTO | ||||
| from bot_api.model.email_string_dto import EMailStringDTO | ||||
| from bot_api.model.o_auth_dto import OAuthDTO | ||||
| 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.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.auth_user import AuthUser | ||||
| from bot_data.model.auth_user_users_relation import AuthUserUsersRelation | ||||
|  | ||||
| _email_regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' | ||||
|  | ||||
|  | ||||
| class AuthService(AuthServiceABC): | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             env: ApplicationEnvironmentABC, | ||||
|             logger: ApiLogger, | ||||
|             bot: DiscordBotServiceABC, | ||||
|             db: DatabaseContextABC, | ||||
|             auth_users: AuthUserRepositoryABC, | ||||
|             users: UserRepositoryABC, | ||||
|             servers: ServerRepositoryABC, | ||||
|             # mailer: MailThread, | ||||
|             mailer: EMailClientABC, | ||||
|             t: TranslatePipe, | ||||
|             auth_settings: AuthenticationSettings, | ||||
|             frontend_settings: FrontendSettings, | ||||
|  | ||||
|     ): | ||||
|         AuthServiceABC.__init__(self) | ||||
|  | ||||
|         self._environment = env | ||||
|         self._logger = logger | ||||
|         self._bot = bot | ||||
|         self._db = db | ||||
|         self._auth_users = auth_users | ||||
|         self._users = users | ||||
|         self._servers = servers | ||||
|         self._mailer = mailer | ||||
|         self._t = t | ||||
|         self._auth_settings = auth_settings | ||||
|         self._frontend_settings = frontend_settings | ||||
|  | ||||
|     @staticmethod | ||||
|     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: | ||||
|         if re.fullmatch(_email_regex, 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.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=CredentialManager.decrypt(self._auth_settings.secret_key) | ||||
|         ) | ||||
|  | ||||
|         return token | ||||
|  | ||||
|     def decode_token(self, token: str) -> dict: | ||||
|         return jwt.decode( | ||||
|             token, | ||||
|             key=CredentialManager.decrypt(self._auth_settings.secret_key), | ||||
|             issuer=self._auth_settings.issuer, | ||||
|             audience=self._auth_settings.audience, | ||||
|             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=CredentialManager.decrypt(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=CredentialManager.decrypt(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 | ||||
|         user.refresh_token_expire_time = datetime.now() + timedelta(days=self._auth_settings.refresh_token_expire_time) | ||||
|         self._auth_users.update_auth_user(user) | ||||
|         self._db.save_changes() | ||||
|         return token | ||||
|  | ||||
|     def _send_link_mail(self, email: str, subject: str, message: str): | ||||
|         url = self._frontend_settings.url | ||||
|         if not url.endswith('/'): | ||||
|             url = f'{url}/' | ||||
|  | ||||
|         self._mailer.connect() | ||||
|         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(str(email)) | ||||
|         mail.subject = subject | ||||
|         mail.body = textwrap.dedent(f"""{message} | ||||
|         {self._t.transform('api.mail.automatic_mail').format(self._environment.application_name, self._environment.environment_name, self._environment.host_name)} | ||||
|         """) | ||||
|  | ||||
|         thr = Thread(target=self._mailer.send_mail, args=[mail]) | ||||
|         thr.start() | ||||
|  | ||||
|     def _send_confirmation_id_to_user(self, user: AuthUser): | ||||
|         url = self._frontend_settings.url | ||||
|         if not url.endswith('/'): | ||||
|             url = f'{url}/' | ||||
|  | ||||
|         self._send_link_mail( | ||||
|             user.email, | ||||
|             self._t.transform('api.auth.confirmation.subject').format(user.first_name, user.last_name), | ||||
|             self._t.transform('api.auth.confirmation.message').format(url, user.confirmation_id) | ||||
|         ) | ||||
|  | ||||
|     def _send_forgot_password_id_to_user(self, user: AuthUser): | ||||
|         url = self._frontend_settings.url | ||||
|         if not url.endswith('/'): | ||||
|             url = f'{url}/' | ||||
|  | ||||
|         self._send_link_mail( | ||||
|             user.email, | ||||
|             self._t.transform('api.auth.forgot_password.subject').format(user.first_name, user.last_name), | ||||
|             self._t.transform('api.auth.forgot_password.message').format(url, user.forgot_password_id) | ||||
|         ) | ||||
|  | ||||
|     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) | ||||
|  | ||||
|     async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO: | ||||
|         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, 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: | ||||
|             self._logger.error(__name__, f'AuthUser not found', e) | ||||
|             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) | ||||
|         return AUT.to_dto(user) if user is not None else None | ||||
|  | ||||
|     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 ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') | ||||
|  | ||||
|         user = AUT.to_db(user_dto) | ||||
|         if self._auth_users.get_all_auth_users().count() == 0: | ||||
|             user.auth_role = AuthRoleEnum.admin | ||||
|  | ||||
|         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') | ||||
|  | ||||
|         try: | ||||
|             user.confirmation_id = uuid.uuid4() | ||||
|             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}') | ||||
|         except Exception as e: | ||||
|             self._logger.error(__name__, f'Cannot add user with E-Mail {user_dto.email}', e) | ||||
|             raise ServiceException(ServiceErrorCode.UnableToAdd, "Invalid E-Mail") | ||||
|  | ||||
|     async def add_auth_user_by_oauth_async(self, dto: OAuthDTO): | ||||
|         db_user = self._auth_users.find_auth_user_by_email(dto.user.email) | ||||
|  | ||||
|         if db_user is None: | ||||
|             raise ServiceException(ServiceErrorCode.InvalidUser, 'User not found') | ||||
|  | ||||
|         if db_user.oauth_id != dto.oauth_id: | ||||
|             raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong OAuthId') | ||||
|  | ||||
|         try: | ||||
|             db_user.first_name = dto.user.first_name | ||||
|             db_user.last_name = dto.user.last_name | ||||
|             db_user.password_salt = uuid.uuid4() | ||||
|             db_user.password = self._hash_sha256(dto.user.password, db_user.password_salt) | ||||
|             db_user.oauth_id = None | ||||
|             db_user.confirmation_id = uuid.uuid4() | ||||
|             self._send_confirmation_id_to_user(db_user) | ||||
|             self._auth_users.update_auth_user(db_user) | ||||
|             self._logger.info(__name__, f'Added auth user with E-Mail: {dto.user.email}') | ||||
|         except Exception as e: | ||||
|             self._logger.error(__name__, f'Cannot add user with E-Mail {dto.user.email}', e) | ||||
|             raise ServiceException(ServiceErrorCode.UnableToAdd, "Invalid E-Mail") | ||||
|  | ||||
|         self._db.save_changes() | ||||
|  | ||||
|     async def add_auth_user_by_discord_async(self, user_dto: AuthUserDTO, dc_id: int) -> OAuthDTO: | ||||
|         db_auth_user = self._auth_users.find_auth_user_by_email(user_dto.email) | ||||
|  | ||||
|         # user exists | ||||
|         if db_auth_user is not None and db_auth_user.users.count() > 0: | ||||
|             # raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') | ||||
|             self._logger.debug(__name__, f'Discord user already exists') | ||||
|             return OAuthDTO(AUT.to_dto(db_auth_user), None) | ||||
|  | ||||
|         # user exists but discord user id not set | ||||
|         elif db_auth_user is not None and db_auth_user.users.count() == 0: | ||||
|             self._logger.debug(__name__, f'Auth user exists but not linked with discord') | ||||
|             # users = self._users.get_users_by_discord_id(user_dto.user_id) | ||||
|             # add auth_user to user refs | ||||
|             db_auth_user.oauth_id = None | ||||
|  | ||||
|         else: | ||||
|             # user does not exists | ||||
|             self._logger.debug(__name__, f'Auth user does not exist') | ||||
|             try: | ||||
|                 user_dto.user_id = self._users.get_users_by_discord_id(user_dto.user_id).single().user_id | ||||
|             except Exception as e: | ||||
|                 self._logger.error(__name__, f'User not found') | ||||
|                 user_dto.user_id = None | ||||
|  | ||||
|             await self.add_auth_user_async(user_dto) | ||||
|             db_auth_user = self._auth_users.get_auth_user_by_email(user_dto.email) | ||||
|             db_auth_user.oauth_id = uuid.uuid4() | ||||
|  | ||||
|         for g in self._bot.guilds: | ||||
|             member = g.get_member(int(dc_id)) | ||||
|             if member is None: | ||||
|                 continue | ||||
|  | ||||
|             server = self._servers.get_server_by_discord_id(g.id) | ||||
|             users = self._users.get_users_by_discord_id(dc_id) | ||||
|             for user in users: | ||||
|                 if user.server.server_id != server.server_id: | ||||
|                     continue | ||||
|                 self._auth_users.add_auth_user_user_rel(AuthUserUsersRelation(db_auth_user, user)) | ||||
|  | ||||
|         self._auth_users.update_auth_user(db_auth_user) | ||||
|         self._db.save_changes() | ||||
|         return OAuthDTO(AUT.to_dto(db_auth_user), db_auth_user.oauth_id) | ||||
|  | ||||
|     async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): | ||||
|         if update_user_dto is None: | ||||
|             raise ServiceException(ServiceErrorCode.InvalidData, f'User is empty') | ||||
|  | ||||
|         if update_user_dto.auth_user is None: | ||||
|             raise ServiceException(ServiceErrorCode.InvalidData, f'Existing user is empty') | ||||
|  | ||||
|         if update_user_dto.new_auth_user is None: | ||||
|             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 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 ServiceException(ServiceErrorCode.InvalidUser, 'User not found') | ||||
|  | ||||
|         if user.confirmation_id is not None: | ||||
|             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.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.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 | ||||
|  | ||||
|         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') | ||||
|  | ||||
|         # update 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) | ||||
|  | ||||
|         self._auth_users.update_auth_user(user) | ||||
|         self._db.save_changes() | ||||
|  | ||||
|     async def update_user_as_admin_async(self, update_user_dto: UpdateAuthUserDTO): | ||||
|         if update_user_dto is None: | ||||
|             raise ServiceException(ServiceErrorCode.InvalidData, f'User is empty') | ||||
|  | ||||
|         if update_user_dto.auth_user is None: | ||||
|             raise ServiceException(ServiceErrorCode.InvalidData, f'Existing user is empty') | ||||
|  | ||||
|         if update_user_dto.new_auth_user is None: | ||||
|             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 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 ServiceException(ServiceErrorCode.InvalidUser, 'User not found') | ||||
|  | ||||
|         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.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.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 | ||||
|  | ||||
|         # update password | ||||
|         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) | ||||
|  | ||||
|         # 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._auth_users.update_auth_user(user) | ||||
|         self._db.save_changes() | ||||
|  | ||||
|     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 ServiceException(ServiceErrorCode.UnableToDelete, f'Cannot delete user by mail {email}') | ||||
|  | ||||
|     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() | ||||
|         except Exception as e: | ||||
|             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'Token invalid', 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') | ||||
|  | ||||
|         db_user = self._auth_users.find_auth_user_by_email(user_dto.email) | ||||
|         if db_user is None: | ||||
|             raise ServiceException(ServiceErrorCode.InvalidUser, f'User not found') | ||||
|  | ||||
|         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') | ||||
|  | ||||
|         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') | ||||
|  | ||||
|         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)) | ||||
|         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 ServiceException(ServiceErrorCode.InvalidData, 'Token not set') | ||||
|  | ||||
|         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') | ||||
|  | ||||
|             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 ServiceException(ServiceErrorCode.InvalidUser, f'User by forgot password id {rp_dto.id} not found') | ||||
|  | ||||
|         if user.confirmation_id is not None: | ||||
|             raise ServiceException(ServiceErrorCode.InvalidUser, f'E-Mail not confirmed') | ||||
|  | ||||
|         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() | ||||
							
								
								
									
										105
									
								
								kdb-bot/src/bot_api/service/discord_service.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								kdb-bot/src/bot_api/service/discord_service.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| 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.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.auth_user_repository_abc import AuthUserRepositoryABC | ||||
| 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: | ||||
|  | ||||
|     def __init__( | ||||
|             self, | ||||
|             bot: DiscordBotServiceABC, | ||||
|             servers: ServerRepositoryABC, | ||||
|             auth: AuthServiceABC, | ||||
|             auth_users: AuthUserRepositoryABC, | ||||
|             users: UserRepositoryABC, | ||||
|     ): | ||||
|         self._bot = bot | ||||
|         self._servers = servers | ||||
|         self._auth = auth | ||||
|         self._auth_users = auth_users | ||||
|         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 = 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() | ||||
|         if token is None or 'email' not in token or 'role' not in token: | ||||
|             raise ServiceException(ServiceErrorCode.InvalidData, 'Token invalid') | ||||
|  | ||||
|         role = AuthRoleEnum(token['role']) | ||||
|         servers = self._servers.get_servers() | ||||
|         if role != AuthRoleEnum.admin: | ||||
|             auth_user = self._auth_users.find_auth_user_by_email(token['email']) | ||||
|             if auth_user is not None: | ||||
|                 user_ids = auth_user.users.select(lambda x: x.server is not None and x.server.server_id) | ||||
|                 servers = servers.where(lambda x: x.server_id in user_ids) | ||||
|  | ||||
|         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() | ||||
|         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) | ||||
|         # filter out servers, where the user not exists | ||||
|         if role != AuthRoleEnum.admin: | ||||
|             auth_user = self._auth_users.find_auth_user_by_email(token['email']) | ||||
|             if auth_user is not None: | ||||
|                 user_ids = auth_user.users.select(lambda x: x.server is not None and x.server.server_id) | ||||
|                 filtered_result.result = filtered_result.result.where(lambda x: x.server_id in user_ids) | ||||
|  | ||||
|         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), | ||||
|             servers.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 | ||||
							
								
								
									
										26
									
								
								kdb-bot/src/bot_api/transformer/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kdb-bot/src/bot_api/transformer/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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.3.dev70' | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| # imports: | ||||
|  | ||||
| VersionInfo = namedtuple('VersionInfo', 'major minor micro') | ||||
| version_info = VersionInfo(major='0', minor='3', micro='dev70') | ||||
							
								
								
									
										38
									
								
								kdb-bot/src/bot_api/transformer/auth_user_transformer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								kdb-bot/src/bot_api/transformer/auth_user_transformer.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| from datetime import datetime, timezone | ||||
|  | ||||
| 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(TransformerABC): | ||||
|  | ||||
|     @staticmethod | ||||
|     def to_db(dto: AuthUserDTO) -> AuthUser: | ||||
|         return AuthUser( | ||||
|             dto.first_name, | ||||
|             dto.last_name, | ||||
|             dto.email, | ||||
|             dto.password, | ||||
|             None, | ||||
|             None, | ||||
|             None, | ||||
|             None, | ||||
|             None, | ||||
|             datetime.now(), | ||||
|             AuthRoleEnum.normal if dto.auth_role is None else AuthRoleEnum(dto.auth_role), | ||||
|             auth_user_id=0 if dto.id is None else dto.id | ||||
|         ) | ||||
|  | ||||
|     @staticmethod | ||||
|     def to_dto(db: AuthUser, password: str = None) -> AuthUserDTO: | ||||
|         return AuthUserDTO( | ||||
|             db.id, | ||||
|             db.first_name, | ||||
|             db.last_name, | ||||
|             db.email, | ||||
|             '' if password is None else password, | ||||
|             db.confirmation_id, | ||||
|             db.auth_role | ||||
|         ) | ||||
							
								
								
									
										24
									
								
								kdb-bot/src/bot_api/transformer/server_transformer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								kdb-bot/src/bot_api/transformer/server_transformer.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| 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 | ||||
|  | ||||
|  | ||||
| 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, 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, | ||||
|         ) | ||||
| @@ -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.dev70' | ||||
| 
 | ||||
| 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='dev70') | ||||
| @@ -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.dev70' | ||||
| 
 | ||||
| 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='dev70') | ||||
| @@ -2,9 +2,9 @@ | ||||
|   "ProjectSettings": { | ||||
|     "Name": "bot-core", | ||||
|     "Version": { | ||||
|       "Major": "1", | ||||
|       "Minor": "0", | ||||
|       "Micro": "0" | ||||
|       "Major": "0", | ||||
|       "Minor": "3", | ||||
|       "Micro": "dev70" | ||||
|     }, | ||||
|     "Author": "Sven Heidemann", | ||||
|     "AuthorEmail": "sven.heidemann@sh-edraft.de", | ||||
| @@ -16,15 +16,13 @@ | ||||
|     "LicenseName": "MIT", | ||||
|     "LicenseDescription": "MIT, see LICENSE for more details.", | ||||
|     "Dependencies": [ | ||||
|       "cpl-core>=2022.10.0" | ||||
|       "cpl-core>=0.3.dev70" | ||||
|     ], | ||||
|     "DevDependencies": [ | ||||
|       "cpl-cli>=2022.10.0" | ||||
|       "cpl-cli==2022.10.0" | ||||
|     ], | ||||
|     "PythonVersion": ">=3.10.4", | ||||
|     "PythonPath": { | ||||
|       "linux": "" | ||||
|     }, | ||||
|     "PythonPath": {}, | ||||
|     "Classifiers": [] | ||||
|   }, | ||||
|   "BuildSettings": { | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user